使用pyodbc通过TVP执行存储过程时遇HY090错误求助
pyodbc调用SQL Server表值参数(TVP)时出现HY090错误的排查思路
在Python中使用pyodbc通过表值参数(TVP)执行SQL Server存储过程时,抛出错误:pyodbc.Error: ('HY090', '[HY090] [Microsoft][ODBC Driver Manager] Invalid string or buffer length (0) (SQLBindParameter)')
该问题仅在PyCharm等编辑器中出现,Jupyter Notebook可正常运行,且pyodbc项目仓库中该问题仍处于开放状态,以下是排查和解决思路。
相关代码定义
TVP类型定义
CREATE TYPE [dbo].[SID_TBL] AS TABLE( [Id] [int] NOT NULL, [SId] [varchar](50) NOT NULL, PRIMARY KEY CLUSTERED ( [SId] ASC )WITH (IGNORE_DUP_KEY = OFF) ) GO
存储过程代码
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [dbo].[GET_STUS] @sid_type dbo.SID_TBL READONLY AS BEGIN SET NOCOUNT ON; select * from dbo.TAB_NAME WITH (NOLOCK) WHERE SID IN (SELECT SId FROM @sid_type) END
Python执行代码
import pyodbc import pandas as pd uid = "admin" pwd = "abcd1234" connection_string = ( r"DRIVER=ODBC Driver 17 for SQL Server;" r"SERVER=xxx;" r"DATABASE=xxx;" f"UID={uid};PWD={pwd};" ) con = pyodbc.connect(connection_string, autocommit=True) cur = con.cursor() s_ids = ["SID_TBL", "dbo"] a = cur.execute("SELECT TOP 1000 SID FROM TAB_A WHERE <conditions>").fetchall() ids = list(enumerate([row[0] for row in a])) s_ids.extend(ids) sql = "EXEC dbo.GET_STUS @sid_type=?" params = (s_ids) result = cur.execute(sql, params).fetchall() print(pd.DataFrame(result[:100]))
排查与解决思路
- 修正TVP参数构造格式:当前代码中
s_ids的构造不符合pyodbc对TVP的严格要求,且params = (s_ids)未正确构造元组(需改为params = (s_ids,)确保是单元素元组)。建议改用官方推荐的显式列定义方式构造TVP:# 正确构造TVP参数 tvp = ( "SID_TBL", # TVP类型名 [("Id", pyodbc.SQL_INTEGER), ("SId", pyodbc.SQL_VARCHAR, 50)], # 列类型定义 ids # 数据行,每个元素为(int, str)格式的元组 ) params = (tvp,) - 检查环境依赖差异:PyCharm与Jupyter可能使用不同Python环境,导致
pyodbc版本或ODBC驱动配置不一致。升级PyCharm环境中的pyodbc到最新稳定版,同时确认ODBC Driver 17 for SQL Server在两个环境中的安装路径、版本一致。 - 调整连接参数:当前连接设置了
autocommit=True,部分场景下自动提交会干扰TVP的参数绑定逻辑。尝试移除autocommit=True,改为手动控制事务:con = pyodbc.connect(connection_string) try: cur = con.cursor() # 执行存储过程逻辑 con.commit() finally: con.close() - 验证数据合法性:HY090错误常与参数长度不匹配有关,检查
ids中是否存在空字符串、长度超过50的SId值,或Id为非整数的异常数据。 - 显式指定参数类型:执行时通过
pyodbc.SQL_SS_TABLE强制指定TVP参数类型,避免驱动自动推断错误:cur.execute(sql, (pyodbc.Parameter("", pyodbc.SQL_SS_TABLE, s_ids),))
内容的提问来源于stack exchange,提问作者user3625533
相关产品推荐
相关产品推荐

