Има драйвер, който наистина поддържа TVP:Pytds . Не се поддържа официално, но има реализация на диалект от трета страна за него:sqlalchemy-pytds . Използвайки ги, можете да извикате вашата съхранена процедура така:
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',)]
По този начин можете да предавате потенциално големи количества данни като параметри:
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',)
Ако от друга страна използвате драйвер, който не поддържа TVP, можете да деклариране на променлива на таблица , вмъкнете стойностите и предайте това като аргумент към вашата процедура:
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',)]
Обърнете внимание, че не можете да използвате никакви методи за изпълнение на много или в крайна сметка ще извикате процедурата за всяка стойност на таблицата поотделно. Ето защо контейнерите се конструират ръчно и стойностите на таблицата се предават като отделни аргументи. Трябва да се внимава да не се форматират никакви аргументи директно в заявката, а вместо това правилното количество контейнери за DB-API. Стойностите на реда са ограничени до максимум 1000 .
Разбира се, би било хубаво, ако основният DB-API драйвер осигури подходяща поддръжка за параметри с таблична стойност, но поне не можах да намеря начин за pymssql, който използва FreeTDS. препратка към TVP в пощенския списък показва ясно, че не се поддържат. Ситуацията не е много по-добра за PyODBC .
Отказ от отговорност:Не съм използвал наистина MS SQL Server преди.