是否可以将 Table 值参数传递给 PYODBC 查询?
Is it possible to pass in Table-Valued Parameters to PYODBC query?
我知道我可以将 TVP 传递给存储过程(因为这是我之前在这里问过的问题)。我现在想知道这是否是我可以在 Python 中做的事情。我不断收到此错误。它说有一个无效的类型,那么参数是否需要知道我传入的是什么类型?
pyodbc.ProgrammingError: ('42000', '[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Column, parameter, or variable #1: Cannot find data type READONLY. (2715) (SQLExecDirectW); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Must declare the table variable "@P1". (1087); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Statement(s) could not be prepared. (8180); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Parameter or variable '@P1' has an invalid data type. (2724)')
from src.data.dbaccess import con
queryString2 = """
SET NOCOUNT ON
DECLARE @t As Table(
PersonName VARCHAR(50)
)
INSERT INTO @t
SELECT *
FROM ?
SELECT * FROM @t
"""
data = ([('Jim', ), ('Bob', )],)
with pyodbc.connect(con.conString) as testCon:
crsr = testCon.execute(queryString2, data)
for row in crsr:
print(row)
** 编辑澄清。在我去年的问题中,我询问是否可以将 TVP 传递给 SQL 服务器存储过程。这一次,程序本身就是我的 Python 代码。这就是我现在要解决的问题。
如问题评论中所述,可以使用 System.Data.SqlClient
从 C# 将 TVP 传递到匿名代码块,如下所示:
var tvpData = new DataTable();
tvpData.Columns.Add(new DataColumn("id", Type.GetType("System.Int32")));
tvpData.Rows.Add(new object[] { 1 });
tvpData.Rows.Add(new object[] { 2 });
using (var con = new SqlConnection(@"Server=127.0.0.1,49242;Database=mydb;Trusted_Connection=True;"))
{
con.Open();
using (var cmd = new SqlCommand("SELECT * FROM @p1", con))
{
SqlParameter tvpParam = cmd.Parameters.AddWithValue("@p1", tvpData);
tvpParam.SqlDbType = SqlDbType.Structured;
tvpParam.TypeName = "dbo.dboListInt"; // an existing user-defined table type
SqlDataReader rdr = cmd.ExecuteReader();
Console.WriteLine("rows returned:");
while (rdr.Read())
{
Console.WriteLine(rdr[0]);
}
}
}
控制台输出:
rows returned:
1
2
但是,这不适用于 C# 使用 System.Data.Odbc
的 ODBC 连接,因为代码依赖于 SqlClient
-特定的 SqlDbType.Structured
类型。
pyodbc 没有类似的机制来指定“结构化”(TVP) 参数类型。相反,它通过它接收的参数的“形状”推断出 table 值参数的存在(例如,由元素元组表示的行,其中一个元素本身就是一个元组列表)。
此外,pyodbc 和 ODBC 驱动程序之间的对话目前并未完全考虑将用户定义类型作为除正常存储过程调用之外的任何参数的参数。 (如果我们可以使用临时存储过程就好了 — CREATE PROCEDURE #myTempSP …
— 但测试表明临时存储过程不能使用用户定义的 table 类型。)
TL;DR — TVP 可以从 C# 传递到匿名代码块,但(目前)不能通过 pyodbc 从 Python 传递。
我知道我可以将 TVP 传递给存储过程(因为这是我之前在这里问过的问题)。我现在想知道这是否是我可以在 Python 中做的事情。我不断收到此错误。它说有一个无效的类型,那么参数是否需要知道我传入的是什么类型?
pyodbc.ProgrammingError: ('42000', '[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Column, parameter, or variable #1: Cannot find data type READONLY. (2715) (SQLExecDirectW); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Must declare the table variable "@P1". (1087); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Statement(s) could not be prepared. (8180); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Parameter or variable '@P1' has an invalid data type. (2724)')
from src.data.dbaccess import con
queryString2 = """
SET NOCOUNT ON
DECLARE @t As Table(
PersonName VARCHAR(50)
)
INSERT INTO @t
SELECT *
FROM ?
SELECT * FROM @t
"""
data = ([('Jim', ), ('Bob', )],)
with pyodbc.connect(con.conString) as testCon:
crsr = testCon.execute(queryString2, data)
for row in crsr:
print(row)
** 编辑澄清。在我去年的问题中,我询问是否可以将 TVP 传递给 SQL 服务器存储过程。这一次,程序本身就是我的 Python 代码。这就是我现在要解决的问题。
如问题评论中所述,可以使用 System.Data.SqlClient
从 C# 将 TVP 传递到匿名代码块,如下所示:
var tvpData = new DataTable();
tvpData.Columns.Add(new DataColumn("id", Type.GetType("System.Int32")));
tvpData.Rows.Add(new object[] { 1 });
tvpData.Rows.Add(new object[] { 2 });
using (var con = new SqlConnection(@"Server=127.0.0.1,49242;Database=mydb;Trusted_Connection=True;"))
{
con.Open();
using (var cmd = new SqlCommand("SELECT * FROM @p1", con))
{
SqlParameter tvpParam = cmd.Parameters.AddWithValue("@p1", tvpData);
tvpParam.SqlDbType = SqlDbType.Structured;
tvpParam.TypeName = "dbo.dboListInt"; // an existing user-defined table type
SqlDataReader rdr = cmd.ExecuteReader();
Console.WriteLine("rows returned:");
while (rdr.Read())
{
Console.WriteLine(rdr[0]);
}
}
}
控制台输出:
rows returned:
1
2
但是,这不适用于 C# 使用 System.Data.Odbc
的 ODBC 连接,因为代码依赖于 SqlClient
-特定的 SqlDbType.Structured
类型。
pyodbc 没有类似的机制来指定“结构化”(TVP) 参数类型。相反,它通过它接收的参数的“形状”推断出 table 值参数的存在(例如,由元素元组表示的行,其中一个元素本身就是一个元组列表)。
此外,pyodbc 和 ODBC 驱动程序之间的对话目前并未完全考虑将用户定义类型作为除正常存储过程调用之外的任何参数的参数。 (如果我们可以使用临时存储过程就好了 — CREATE PROCEDURE #myTempSP …
— 但测试表明临时存储过程不能使用用户定义的 table 类型。)
TL;DR — TVP 可以从 C# 传递到匿名代码块,但(目前)不能通过 pyodbc 从 Python 传递。