Der er en driver, der virkelig understøtter TVP'er:Pytds . Det er ikke officielt understøttet, men der er en 3. parts dialektimplementering til det:sqlalchemy-pytds . Ved at bruge dem kan du kalde din lagrede procedure sådan:
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',)]
På denne måde kan du videregive potentielt store mængder data som parametre:
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',)
Hvis du på den anden side bruger en driver, der ikke understøtter TVP'er, kan du erklære en tabelvariabel , indsæt værdierne, og giv det som argumentet til din procedure:
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',)]
Bemærk, at du ikke kan bruge nogen eksekveringsmetoder, eller du ender med at kalde proceduren for hver tabelværdi separat. Det er grunden til, at pladsholderne konstrueres manuelt, og tabelværdierne videregives som individuelle argumenter. Man skal passe på ikke at formatere nogen argumenter direkte ind i forespørgslen, men det korrekte antal pladsholdere til DB-API i stedet. Rækkeværdier er begrænset til en maksimalt 1000 .
Det ville selvfølgelig være rart, hvis den underliggende DB-API-driver gav ordentlig support til tabelværdisatte parametre, men i det mindste kunne jeg ikke finde en måde til pymssql, som bruger FreeTDS. En reference til TVP'er på mailinglisten gør det klart, at de ikke understøttes. Situationen er ikke meget bedre for PyODBC .
Ansvarsfraskrivelse:Jeg har ikke rigtig brugt MS SQL Server før.