使用 pymssql 连接数据库(GBK 编码)乱码问题的完美解决方案

背景

在对接国内一些早期开发的 ERP、物流、一卡通等系统时,SQL Server 数据库中的中文数据常常采用 GBK 编码 存储。

然而 pymssql 在连接时若使用 charset='utf8',读取出来的中文字符会呈现为"乱码"------实际上是 UTF-8 解码 GBK 字节导致的现象。

直接修改连接字符集为 charset='gbk' 又可能引发其他字段(如数字、英文、特殊符号)的异常,甚至导致连接失败。

本文将结合真实案例,提供一个 稳定、可复现 的乱码修复方案。

问题现象

某物流系统数据库,表结构如下:

复制代码
CREATE TABLE t_record (
    s_vehicle_no VARCHAR(50),   -- 车牌号(中文)
    s_remark VARCHAR(200),      -- 备注(中文)
    ...
);

使用 pymssql 查询结果:

复制代码
{
    '车牌号': '沪A12345',   # 实际应为"沪A12345"
    '备注': '包装损坏'    # 实际应为"包装损坏"
}

这类乱码特征明显:英文字符正常,中文字符变为多字节乱码

错误尝试

1. 直接修改连接字符集

复制代码
DB_CONFIG = {
    'charset': 'gbk'
}

结果:

  • 某些字符集下查询报错(如 UnicodeDecodeError

  • 数字、英文可能被错误转换

  • pymssql 内部处理机制不兼容

2. 手动 encode/decode

复制代码
value.encode('latin-1').decode('gbk')

有效,但需要明确区分哪些字段是中文、哪些不是,无法通用。

最终解决方案

核心思路

统一使用 charset='utf8' 连接 ,在读取后将所有字符串字段通过 "拉丁-1 中转" 方式还原为 GBK 原始字节,再解码为正确的中文。

原理说明

  • pymssqlutf8 连接模式下,将数据库返回的字节流按 UTF-8 解码

  • 若数据库实际存储为 GBK,则会被错误解码成"假 Unicode 字符串"。

  • 修复过程:

    1. 将该字符串按 latin-1(单字节无损编码)转回原始字节

    2. GBK 重新解码

    3. 得到真实中文

    import pymssql

    def fix_gbk_text(text):
    """修复 pymssql + utf8 模式下的 GBK 乱码"""
    if text is None or not isinstance(text, str):
    return text
    try:
    return text.encode('latin-1').decode('GBK')
    except:
    return text

    def get_clean_data(order_no):
    conn = pymssql.connect(
    server='192.168..',
    user='',
    password='
    ',
    database='******',
    charset='utf8'
    )
    cursor = conn.cursor(as_dict=True)

    复制代码
      sql = """
      SELECT TOP 1
          s_vehicle_no AS 车牌号,
          s_remark AS 备注
      FROM t_record
      WHERE s_code = %s
      """
    
      cursor.execute(sql, (order_no,))
      row = cursor.fetchone()
    
      if row:
          clean_row = {}
          for key, value in row.items():
              if isinstance(value, str):
                  clean_row[key] = fix_gbk_text(value)
              else:
                  clean_row[key] = value
          return clean_row
      else:
          return None

验证结果

修复前:

复制代码
{
    '车牌号': '沪A12345',
    '备注': '包装损坏'
}

修复后:

复制代码
{
    '车牌号': '沪A12345',
    '备注': '包装损坏'
}

适用场景

该方案适用于以下情况:

  • SQL Server 数据库,中文采用 GBK / GB2312 / GB18030 编码

  • 使用 pymssql 且无法修改数据库端编码

  • 需要同时保留数字、英文、Decimal、日期等非字符串类型的原始格式


注意事项

  1. 仅修复字符串字段:数字、布尔值、Decimal 等类型无需处理,直接保留。

  2. 性能影响极小:仅在查询结果集上做一次遍历修复,适合中小规模数据。

  3. 兼容性好:修复失败的字段会返回原值,不会导致程序崩溃。

  4. 适用于多表 JOIN:无论多少字段,统一遍历修复即可。


总结

在国内企业信息化系统中,GBK 编码的中文数据库依然广泛存在。
pymssql 默认的 UTF-8 连接方式虽然保证了跨平台兼容性,但在处理老系统数据时容易出现乱码。

本文提供了一种 无侵入、易封装、可复用 的修复模式,能够在不修改数据库、不改变连接配置的前提下,干净地还原真实中文数据。

该方案已在实际生产环境中稳定运行,适用于各类 GBK 编码的中文数据提取场景。

相关推荐
m0_596749095 小时前
JavaScript中手动实现一个new操作符的底层逻辑
jvm·数据库·python
多加点辣也没关系5 小时前
Redis 的安装(详细教程)
数据库·redis·缓存
数据库小学妹6 小时前
数据库连接池避坑指南:告别“连接超时”与“资源耗尽”,让系统跑得更快!
数据库·redis·sql·mysql·缓存·dba
dishugj6 小时前
HANA 数据库备份与恢复
数据库·oracle
前进的李工6 小时前
EXPLAIN输出格式全解析:JSON、TREE与可视化
开发语言·数据库·mysql·性能优化·explain
難釋懷7 小时前
Redis网络模型-IO多路复用模型-poll模式
网络·数据库·redis
dFObBIMmai7 小时前
如何在 CSS 中实现元素的绝对定位,使其不受窗口尺寸变化影响
jvm·数据库·python
treesforest7 小时前
IP精准定位服务:从城市轮廓到街道坐标,技术如何重塑空间感知
网络·数据库·网络协议·tcp/ip·ip
大明者省7 小时前
宝塔开了端口,Ubuntu 还得开相应端口才能打通
服务器·数据库·ubuntu
Teable任意门互动8 小时前
AI原生开源多维表格有哪些?主流开源多维表格对比解析
数据库·开源·excel·钉钉·飞书·开源软件·ai-native