Istnieje sterownik, który naprawdę obsługuje programy TVP:Pytds . Nie jest oficjalnie obsługiwany, ale istnieje dla niego implementacja dialektu innej firmy:sqlalchemy-pytds . Korzystając z nich, możesz wywołać procedurę składowaną w następujący sposób:
In [1]: engine.execute(DDL("CREATE TYPE [dbo].[StringTable] AS TABLE([strValue] [nvarchar](max) NULL)"))
Out[1]: <sqlalchemy.engine.result.ResultProxy at 0x7f235809ae48>
In [2]: engine.execute(DDL("CREATE PROC test_proc (@pArg [StringTable] READONLY) AS BEGIN SELECT * FROM @pArg END"))
Out[2]: <sqlalchemy.engine.result.ResultProxy at 0x7f2358027b70>
In [3]: arg = ['Name One', 'Name Two']
In [4]: import pytds
In [5]: tvp = pytds.TableValuedParam(type_name='StringTable',
...: rows=((x,) for x in arg))
In [6]: engine.execute('EXEC test_proc %s', (tvp,))
Out[6]: <sqlalchemy.engine.result.ResultProxy at 0x7f294e699e10>
In [7]: _.fetchall()
Out[7]: [('Name One',), ('Name Two',)]
W ten sposób możesz przekazać potencjalnie duże ilości danych jako parametry:
In [21]: tvp = pytds.TableValuedParam(type_name='StringTable',
...: rows=((str(x),) for x in range(100000)))
In [22]: engine.execute('EXEC test_proc %s', (tvp,))
Out[22]: <sqlalchemy.engine.result.ResultProxy at 0x7f294c6e9f98>
In [23]: _.fetchall()[-1]
Out[23]: ('99999',)
Jeśli z drugiej strony używasz sterownika, który nie obsługuje programów TVP, możesz deklaruj zmienną tabeli , wstaw wartości i przekaż to jako argument do Twojej procedury:
In [12]: engine.execute(
...: """
...: DECLARE @pArg AS [StringTable];
...: INSERT INTO @pArg VALUES {placeholders};
...: EXEC test_proc @pArg;
...: """.format(placeholders=",".join(["(%s)"] * len(arg))),
...: tuple(arg))
...:
Out[12]: <sqlalchemy.engine.result.ResultProxy at 0x7f23580f2908>
In [15]: _.fetchall()
Out[15]: [('Name One',), ('Name Two',)]
Zwróć uwagę, że nie możesz użyć żadnych metod executewielu, w przeciwnym razie wywołasz procedurę osobno dla każdej wartości tabeli. Dlatego symbole zastępcze są konstruowane ręcznie, a wartości tabeli przekazywane jako pojedyncze argumenty. Należy uważać, aby nie formatować żadnych argumentów bezpośrednio w zapytaniu, ale zamiast tego poprawną liczbę symboli zastępczych dla DB-API. Wartości wierszy są ograniczone do maksymalnie 1000 .
Byłoby oczywiście miło, gdyby bazowy sterownik DB-API zapewniał odpowiednią obsługę parametrów o wartościach tabelarycznych, ale przynajmniej nie mogłem znaleźć sposobu na pymssql, który używa FreeTDS. odniesienie do TVP na liście dyskusyjnej wyjaśnia, że nie są obsługiwane. Sytuacja jest niewiele lepsza dla PyODBC .
Zastrzeżenie:Tak naprawdę nie używałem wcześniej MS SQL Server.