pyodbc执行sqlserver存储过程--如何为User Defined传入DEFAULT参数Table
Pyodbc execute sqlserver stored procedure -- how to pass in DEFAULT parameter for User Defined Table
我有以下用户定义的 Table,如您所见,我有 2 列具有默认值:
CREATE TYPE dbo.mytable AS TABLE(
[MYID] BIGINT,
[MYVALUE] INT,
[CURRENT_INDEX] BIT default 1,
[CURRENT_TS] DATETIME2(7) default CURRENT_TIMESTAMP
)
这是我的虚拟存储过程:
CREATE PROCEDURE MY_PROCEDURE @temp_table dbo.mytable READONLY
AS
BEGIN
INSERT INTO dbo.main_table
(MYID,MYVALUE,CURRENT_INDEX,CURRENT_TS)
SELECT * FROM @temp_table as tt
END
GO
我正在尝试通过使用 pyodbc 从 Python 传入用户定义的 table 来执行此存储过程。如您所见,我试图仅将 MYID
和 MYVALUE
传递给存储过程,并且我希望用户定义的 table 能够在不传递的情况下自动填充默认值。
def _insert_stored_procedure(cursor, table_val_parameter: list):
"""
Call the stored procedure for table insert/update
"""
cursor.execute("{CALL MY_PROCEDURE (?)}",
(table_val_parameter,))
cursor.commit()
conn = DBCONN_Singleton(connection_type)
cursor = conn.cursor
_tvp = [[1,100],[2,150],[3,200]]
_insert_update_stored_procedure(cursor, _tvp)
很明显以上失败并出现以下错误:
[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Trying to pass a table-valued parameter with 2 column(s) where the corresponding user-defined table type requires 4 column(s). (500) (SQLParamData)')
所以我的问题是:
鉴于上述情况,我如何只传入 MYID
和 MYVALUE
让存储过程或用户定义 table 自动填充DEFAULT
缺失值
如果没有,我如何从python传递默认值,例如,我想做这样的事情:
_tvp = [[1,100,DEFAULT,DEFAULT],[2,150,DEFAULT,DEFAULT],[3,200,DEFAULT,DEFAULT]]
AFAIK 目前没有机制——例如,像 pyodbc.DEFAULT
这样的东西——来表示文字 DEFAULT
,就像存储过程调用一样。此解决方法有点笨拙,但似乎可以完成工作:
cnxn.autocommit = True
crsr = cnxn.cursor()
crsr.fast_executemany = True
# (remove previous test data)
crsr.execute("TRUNCATE TABLE dbo.main_table")
data = ([1 ,100], [2, 150],[3, 200])
crsr.execute("CREATE TABLE #temp (MYID bigint, MYVALUE int)")
crsr.executemany("INSERT INTO #temp (MYID, MYVALUE) VALUES (?, ?)", data)
sql = """\
SET NOCOUNT ON;
DECLARE @tvp dbo.mytable;
INSERT INTO @tvp (MYID, MYVALUE) SELECT MYID, MYVALUE FROM #temp
EXEC dbo.MY_PROCEDURE @tvp
"""
crsr.execute(sql)
pprint(crsr.execute("SELECT * FROM dbo.main_table").fetchall())
"""console output:
[(1, 100, True, datetime.datetime(2020, 7, 22, 12, 17, 14, 623333)),
(2, 150, True, datetime.datetime(2020, 7, 22, 12, 17, 14, 623333)),
(3, 200, True, datetime.datetime(2020, 7, 22, 12, 17, 14, 623333))]
"""
除了@Gord Thompson的回答,我还找到了另一种方法,其实很简单:
首先,从用户定义的 table 类型中删除默认值:
CREATE TYPE dbo.mytable AS TABLE(
[MYID] BIGINT,
[MYVALUE] INT)
存储过程:
CREATE PROCEDURE MY_PROCEDURE @temp_table dbo.mytable READONLY
AS
BEGIN
INSERT INTO dbo.main_table
(MYID,MYVALUE,CURRENT_INDEX,CURRENT_TS)
SELECT *,1,CURRENT_TIMESTAMP FROM @temp_table as tt
END
GO
我有以下用户定义的 Table,如您所见,我有 2 列具有默认值:
CREATE TYPE dbo.mytable AS TABLE(
[MYID] BIGINT,
[MYVALUE] INT,
[CURRENT_INDEX] BIT default 1,
[CURRENT_TS] DATETIME2(7) default CURRENT_TIMESTAMP
)
这是我的虚拟存储过程:
CREATE PROCEDURE MY_PROCEDURE @temp_table dbo.mytable READONLY
AS
BEGIN
INSERT INTO dbo.main_table
(MYID,MYVALUE,CURRENT_INDEX,CURRENT_TS)
SELECT * FROM @temp_table as tt
END
GO
我正在尝试通过使用 pyodbc 从 Python 传入用户定义的 table 来执行此存储过程。如您所见,我试图仅将 MYID
和 MYVALUE
传递给存储过程,并且我希望用户定义的 table 能够在不传递的情况下自动填充默认值。
def _insert_stored_procedure(cursor, table_val_parameter: list):
"""
Call the stored procedure for table insert/update
"""
cursor.execute("{CALL MY_PROCEDURE (?)}",
(table_val_parameter,))
cursor.commit()
conn = DBCONN_Singleton(connection_type)
cursor = conn.cursor
_tvp = [[1,100],[2,150],[3,200]]
_insert_update_stored_procedure(cursor, _tvp)
很明显以上失败并出现以下错误:
[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Trying to pass a table-valued parameter with 2 column(s) where the corresponding user-defined table type requires 4 column(s). (500) (SQLParamData)')
所以我的问题是:
鉴于上述情况,我如何只传入
缺失值MYID
和MYVALUE
让存储过程或用户定义 table 自动填充DEFAULT如果没有,我如何从python传递默认值,例如,我想做这样的事情:
_tvp = [[1,100,DEFAULT,DEFAULT],[2,150,DEFAULT,DEFAULT],[3,200,DEFAULT,DEFAULT]]
AFAIK 目前没有机制——例如,像 pyodbc.DEFAULT
这样的东西——来表示文字 DEFAULT
,就像存储过程调用一样。此解决方法有点笨拙,但似乎可以完成工作:
cnxn.autocommit = True
crsr = cnxn.cursor()
crsr.fast_executemany = True
# (remove previous test data)
crsr.execute("TRUNCATE TABLE dbo.main_table")
data = ([1 ,100], [2, 150],[3, 200])
crsr.execute("CREATE TABLE #temp (MYID bigint, MYVALUE int)")
crsr.executemany("INSERT INTO #temp (MYID, MYVALUE) VALUES (?, ?)", data)
sql = """\
SET NOCOUNT ON;
DECLARE @tvp dbo.mytable;
INSERT INTO @tvp (MYID, MYVALUE) SELECT MYID, MYVALUE FROM #temp
EXEC dbo.MY_PROCEDURE @tvp
"""
crsr.execute(sql)
pprint(crsr.execute("SELECT * FROM dbo.main_table").fetchall())
"""console output:
[(1, 100, True, datetime.datetime(2020, 7, 22, 12, 17, 14, 623333)),
(2, 150, True, datetime.datetime(2020, 7, 22, 12, 17, 14, 623333)),
(3, 200, True, datetime.datetime(2020, 7, 22, 12, 17, 14, 623333))]
"""
除了@Gord Thompson的回答,我还找到了另一种方法,其实很简单:
首先,从用户定义的 table 类型中删除默认值:
CREATE TYPE dbo.mytable AS TABLE(
[MYID] BIGINT,
[MYVALUE] INT)
存储过程:
CREATE PROCEDURE MY_PROCEDURE @temp_table dbo.mytable READONLY
AS
BEGIN
INSERT INTO dbo.main_table
(MYID,MYVALUE,CURRENT_INDEX,CURRENT_TS)
SELECT *,1,CURRENT_TIMESTAMP FROM @temp_table as tt
END
GO