You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pandas和SQLAlchemy读取SQL Server表时的编码问题

解决Pandas读取SQL Server表导出Parquet时的UTF-16解码错误

问题场景

使用Pandas结合SQLAlchemy读取SQL Server表并导出为Parquet文件时,大部分表流程正常,但读取某张表时触发编码错误:

'utf-16-le' codec can't decode bytes in position 46-47: illegal UTF-16 surrogate

相关代码如下:

for tbl in table:
    df = pd.read_sql_table(f'{tbl[0]}', mssql_engine())
    filename = (tbl[0]+'.parquet')
    path = (tmppath+filename)

    df.to_parquet(path, compression=None)

尝试在create_engine中设置charset=latin1(对应目标表中某列的排序规则SQL_Latin1_General_CP1_CI_AI),但错误依然存在:

def mssql_engine(user=sqluser, password=sqlpassword, host=sqlserver, db=sqldatabase):
    engine = create_engine(
        f'mssql+pyodbc://{user}:{password}@{host}/{db}?driver=SQL+Server&charset=latin1')
    return engine

目标表中出问题的列信息:

{'name': 'Column', 'type': VARCHAR(length=2, collation='SQL_Latin1_General_CP1_CI_AI'), 'nullable': True, 'default': None, 'autoincrement': False}

原因分析

SQLAlchemy的charset参数对pyodbc驱动的编码控制有限,pyodbc会优先根据SQL Server列的排序规则自动处理字符编码转换,但在某些情况下(比如列中存在不符合UTF-16规则的字节序列),会错误地将Latin1编码的字节按UTF-16解析,导致解码失败。

解决方案

方案1:强制pyodbc返回字节并手动解码

修改SQLAlchemy连接参数,关闭自动Unicode转换,让pyodbc直接返回字节数据,再手动用Latin1解码目标列:

def mssql_engine(user=sqluser, password=sqlpassword, host=sqlserver, db=sqldatabase):
    engine = create_engine(
        f'mssql+pyodbc://{user}:{password}@{host}/{db}?driver=SQL+Server&unicode_results=False')
    return engine

读取表后对目标列进行解码:

df = pd.read_sql_table(f'{tbl[0]}', mssql_engine())
# 仅对受影响的Latin1编码列处理
df['Column'] = df['Column'].str.decode('latin1')

方案2:通过SQL查询显式转换编码

使用read_sql_query替代read_sql_table,在SQL语句中显式指定列的编码转换:

query = f"""
SELECT 
    CONVERT(VARCHAR(2), [Column] COLLATE SQL_Latin1_General_CP1_CI_AI) AS [Column],
    -- 列出表中其他列
    [OtherColumn1], [OtherColumn2]
FROM {tbl[0]}
"""
df = pd.read_sql_query(query, mssql_engine())

方案3:忽略解码错误(临时应急)

如果无需保留非法字节的内容,可以在解码时忽略错误:

df = pd.read_sql_table(f'{tbl[0]}', mssql_engine())
df['Column'] = df['Column'].apply(
    lambda x: x.encode('latin1').decode('utf-16-le', errors='ignore') 
    if pd.notnull(x) else x
)

内容的提问来源于stack exchange,提问作者Megalitik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 17:15:13