Ada driver yang sangat mendukung TVP:Pytds . Ini tidak didukung secara resmi, tetapi ada implementasi dialek pihak ketiga untuknya:sqlalchemy-pytds . Dengan menggunakannya, Anda dapat memanggil prosedur tersimpan Anda seperti ini:
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',)]
Dengan cara ini Anda dapat mengirimkan data dalam jumlah besar yang berpotensi sebagai parameter:
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',)
Jika sebaliknya Anda menggunakan driver yang tidak mendukung TVP, Anda dapat deklarasikan variabel tabel , masukkan nilainya, dan teruskan itu sebagai argumen untuk prosedur Anda:
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',)]
Perhatikan bahwa Anda tidak dapat menggunakan metode eksekusi banyak, atau Anda akan berakhir dengan memanggil prosedur untuk setiap nilai tabel secara terpisah. Itulah sebabnya placeholder dibuat secara manual dan nilai tabel diteruskan sebagai argumen individual. Harus berhati-hati untuk tidak memformat argumen apa pun secara langsung ke kueri, tetapi jumlah placeholder yang benar untuk DB-API sebagai gantinya. Nilai baris dibatasi pada maksimum 1000 .
Tentu saja akan menyenangkan, jika driver DB-API yang mendasarinya memberikan dukungan yang tepat untuk parameter bernilai tabel, tetapi setidaknya saya tidak dapat menemukan cara untuk pymssql, yang menggunakan FreeTDS. Referensi ke TVP di milis memperjelas bahwa mereka tidak didukung. Situasinya tidak jauh lebih baik untuk PyODBC .
Penafian:Saya belum pernah menggunakan MS SQL Server sebelumnya.