sql >> Databasteknik >  >> RDS >> Sqlserver

Hur man anropar lagrad procedur med SQLAlchemy som kräver en användardefinierad tabellparameter

Det finns en drivrutin som verkligen stöder TVP:er:Pytds . Det stöds inte officiellt, men det finns en dialektimplementering från tredje part för det:sqlalchemy-pytds . Genom att använda dem kan du kalla din lagrade procedur så här:

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å så sätt kan du skicka potentiellt stora mängder data som parametrar:

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

Om du å andra sidan använder en drivrutin som inte stöder TVP, kan du deklarera en tabellvariabel , infoga värdena och överlämna det som argumentet till din procedur:

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

Observera att du inte kan använda några exekveringsmetoder, annars kommer du att anropa proceduren för varje tabellvärde separat. Det är därför platshållarna konstrueras manuellt och tabellvärdena skickas som individuella argument. Var noga med att inte formatera några argument direkt i frågan, utan rätt antal platshållare för DB-API istället. Radvärden är begränsade till en maximalt 1 000 .

Det skulle naturligtvis vara trevligt om den underliggande DB-API-drivrutinen gav ordentligt stöd för tabellvärderade parametrar, men jag kunde åtminstone inte hitta ett sätt för pymssql, som använder FreeTDS. En hänvisning till TVPs på e-postlistan gör det klart att de inte stöds. Situationen är inte mycket bättre för PyODBC .

Ansvarsfriskrivning:Jag har inte riktigt använt MS SQL Server tidigare.



  1. Hur hämtar man värden från en databas?

  2. Behöver vi ange inte null för primärnyckeln? Oracle/SQL

  3. Verktyg:Generera PL/SQL-procedur för att exportera data från en tabell på 2 minuter

  4. geometri känns inte igen som parameter till Find_SRID