sql >> Database >  >> RDS >> Sqlserver

Hoe een opgeslagen procedure aan te roepen met SQLAlchemy waarvoor een door de gebruiker gedefinieerde tabelparameter vereist is?

Er is een driver die TVP's echt ondersteunt:Pytds . Het wordt niet officieel ondersteund, maar er is een dialectimplementatie van derden voor:sqlalchemy-pytds . Als u ze gebruikt, kunt u uw opgeslagen procedure als volgt oproepen:

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',)]

Op deze manier kunt u potentieel grote hoeveelheden gegevens doorgeven als parameters:

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',)

Als u daarentegen een stuurprogramma gebruikt dat geen TVP's ondersteunt, kunt u declareer een tabelvariabele , voeg de waarden in en geef dat door als argument naar uw 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',)]

Merk op dat u geen executemany-methoden kunt gebruiken, anders zult u de procedure voor elke tabelwaarde afzonderlijk aanroepen. Daarom worden de tijdelijke aanduidingen handmatig samengesteld en worden de tabelwaarden als afzonderlijke argumenten doorgegeven. Er moet voor worden gezorgd dat geen argumenten rechtstreeks in de query worden opgemaakt, maar in plaats daarvan het juiste aantal tijdelijke aanduidingen voor de DB-API. Rijwaarden zijn beperkt tot een maximaal 1000 .

Het zou natuurlijk leuk zijn als de onderliggende DB-API-driver de juiste ondersteuning zou bieden voor tabelparameters, maar ik kon tenminste geen manier vinden voor pymssql, dat FreeTDS gebruikt. Een verwijzing naar TVP's op de mailinglijst maakt duidelijk dat ze niet worden ondersteund. De situatie is niet veel beter voor PyODBC .

Disclaimer:ik heb MS SQL Server nog niet echt gebruikt.



  1. Aangepaste aggregatiefunctie

  2. Django-implementatie van standaardwaarde in database

  3. Waarom moet de MySQL-connector nog steeds op de computer zijn geïnstalleerd, ondanks dat ik de juiste NuGet-pakketten heb?

  4. MySQL:u kunt de doeltabel voor update niet specificeren in de FROM-clausule