使用Pandas读取Parquet导入SQL Server时遇TDS协议错误求助
问题描述
尝试用Pandas读取Parquet文件并导入Microsoft SQL Server表时,遇到pyodbc.ProgrammingError,提示参数16不是有效的float类型。已确认无无效float值且目标列均为可空类型,无法定位问题。
代码片段
from pandas import read_parquet import numpy as np df = read_parquet('<file-path>', engine='fastparquet') df = df.fillna(value=np.nan) cols = "],[&".join([str(i) for i in df.columns.tolist()]) for index, row in df.iterrows(): sql = "INSERT INTO " + obj.table + "([" + cols + "]) VALUES (" + "?," * (len(row) - 1) + "?)" try: cur.execute(sql, tuple(row)) except: print(sql, tuple(row)) tb.print_exc() break conn.commit()
错误调试信息
<For debugging> INSERT INTO table(col1, col2 col3 ...) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) (294958881.0, 0.0, 71142106.0, 5.0, 4.0, 4.0, 0.0, 4804102.0, 1.0, 1.0, Timestamp('2020-01-30 12:00:01.590000'), Timestamp('2020-01-30 12:05:12.480000'), nan, nan, nan, nan, nan, Timestamp('2020-01-30 12:05:00'), Timestamp('2020-01-30 12:05:12.420000'), Timestamp('2020-01-30 12:05:12.420000'), nan, nan, nan, 130864939.0, 1.0, 1.0, 0.0, 0.0, 253199575.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, nan, 0.0, 0.0, nan, nan, nan, nan, 1.0, 1.0, '63D861B100000001', Timestamp('2023-01-31 00:32:49.265000'), Timestamp('2023-01-31 00:32:49')) Traceback (most recent call last): File "C:\temp\file.py", line 293, in method01 cur.execute(sql, tuple(row)) pyodbc.ProgrammingError: ('42000', '[42000] [Microsoft][SQL Server Native Client 11.0][SQL Server]The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 16 (""): The supplied value is not a valid instance of data type float. Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision. (8023) (SQLExecDirectW)')
解决建议
修复列名拼接的语法错误
代码中cols = "],[&".join([str(i) for i in df.columns.tolist()])的分隔符错误,应该使用"],["替代"],[&"。错误的分隔符会生成无效的列名(比如[col1],[&col2]),导致SQL语句中列与参数的对应关系完全混乱,这是引发错误的核心原因。修正后代码:cols = "], [".join([str(i) for i in df.columns.tolist()]) sql = f"INSERT INTO {obj.table}([{cols}]) VALUES ({','.join(['?']*len(row))})"同时建议用
','.join(['?']*len(row))替代手动拼接占位符,避免计数错误。替换
np.nan为None适配SQL NULL
SQL Server的NULL值对应Python的None,而非np.nan。部分ODBC驱动对np.nan的处理存在兼容性问题,导致被误判为无效float值。修正填充空值的代码:df = df.fillna(value=None)转换Pandas Timestamp为Python原生datetime
Pandas的Timestamp类型在通过pyodbc传递时可能存在解析问题,建议批量转换为Python原生datetime类型:for col in df.select_dtypes(include=['datetime64']).columns: df[col] = df[col].dt.to_pydatetime()改用Pandas原生
to_sql批量插入
逐行iterrows插入效率极低且容易出错,推荐使用Pandas内置的to_sql方法,它会自动处理类型映射、空值和批量插入,可靠性更高:from sqlalchemy import create_engine # 构建SQL Server连接字符串(替换为你的实际信息) conn_str = "mssql+pyodbc://<username>:<password>@<server>/<database>?driver=SQL+Server+Native+Client+11.0" engine = create_engine(conn_str) # 导入数据,if_exists='append'表示追加到现有表 df.to_sql(obj.table, engine, if_exists='append', index=False, chunksize=1000)
内容的提问来源于stack exchange,提问作者S.D.
相关产品推荐
相关产品推荐

