mssql-python 的错误处理与 SQLSTATE 代码

mssql-python 驱动定义了标准的异常层级结构、常见的错误处理模式以及针对 SQL Server 和 Azure SQL 的 SQLSTATE 代码映射。

异常层次结构

mssql-python驱动遵循 DB-API 2.0(PEP 249)异常层级结构:

Exception (builtins)
├── Warning
└── Error
    ├── InterfaceError
    └── DatabaseError
        ├── DataError
        ├── OperationalError
        ├── IntegrityError
        ├── InternalError
        ├── ProgrammingError
        └── NotSupportedError

ConnectionStringParseError (standalone, not part of hierarchy)

异常说明

抓住最符合你情况的具体例外。 例如,捕捉 IntegrityError / 操作上的INSERT约束违规,以及ProgrammingError开发过程中的 SQLUPDATE 语法问题。 基础 Error 职业只能作为备选。

Exception 引发时
Warning 数据库中的非致命警告。
Error 所有数据库错误的基础类。
InterfaceError 错误与数据库接口(驱动程序)有关,而非数据库本身。
DatabaseError 数据库相关的错误。
DataError 由于处理数据出现问题(除以零,值超出范围)导致的错误。
OperationalError 与数据库操作相关的错误(连接丢失、内存分配、事务错误)。
IntegrityError 当数据库完整性受到影响时会出现错误(如外键违规、唯一约束)。
InternalError 内部数据库错误(光标无效,交易不同步)。
ProgrammingError 编程错误(语法错误、找不到表格、参数数量错误)。
NotSupportedError 数据库或驱动不支持此功能。
ConnectionStringParseError 连接字符串语法无效或未知关键词。

基本错误处理

使用try-exclusion块来处理数据库错误:

import mssql_python

try:
    conn = mssql_python.connect(connection_string)
    cursor = conn.cursor()
    cursor.execute("INSERT INTO Production.Product (Name) VALUES (%(name)s)", {"name": "Test"})
    conn.commit()
except mssql_python.IntegrityError as e:
    print(f"Constraint violation: {e}")
    conn.rollback()
except mssql_python.ProgrammingError as e:
    print(f"SQL syntax error: {e}")
except mssql_python.OperationalError as e:
    print(f"Connection or operational error: {e}")
except mssql_python.Error as e:
    print(f"Database error: {e}")
finally:
    if 'conn' in locals():
        conn.close()

通过连接的访问异常

你可以通过连接实例捕捉异常情况:

try:
    cursor.execute("INVALID SQL")
except conn.ProgrammingError as e:
    print(f"Caught via connection: {e}")

错误消息结构

MSSQL-Python 异常对象会暴露出来自驱动程序 Exception 基类的三个属性:

Attribute Source 描述
driver_error Python 驱动 由 SQLSTATE 选择的标准化英文文本从 ODBC 返回(例如, "Communication link failure""Invalid authorization specification""Syntax error or access violation")。 跨版本稳定;可以安全地匹配子串。
ddbc_error 直接数据库连接(DDBC) 服务器端消息,通常以 [Microsoft][SQL Server]为前缀。 格式不是稳定的合同。
message 组成 f"Driver Error: {driver_error}; DDBC Error: {ddbc_error}"。 这就是回归的。str(exc)
try:
    cursor.execute("SELECT * FROM no_such_table;")
except mssql_python.ProgrammingError as exc:
    print(exc.driver_error)  # Base table or view not found
    print(exc.ddbc_error)    # [Microsoft][SQL Server]Invalid object name 'no_such_table'.
    print(exc)               # Driver Error: Base table or view not found; DDBC Error: ...

SQL Server 引擎的错误编号(例如 20840501)不会作为属性暴露,也不会可靠地嵌入在任何字符串中。 通过例外子类加上 driver_error 文本来分类错误。 关于Azure SQL限速,请参见Retry logic

SQLSTATE 分类

mssql-python 使用 ODBC 返回的 SQLSTATE 来选择 Python 异常子类和文本driver_error。 完整的 SQLSTATE →异常映射已在 exceptions.py 驱动源代码中。 下一节列出了SQL Server和Azure SQL中最常出现的SQLSTATE。

连接错误

来自 mssql_python.connect()mssql_python.OperationalError连接失败与其他连接失败相同:

import mssql_python

try:
    conn = mssql_python.connect(
        "Server=unreachable-server.database.windows.net;"
        "Database=<database>;"
        "Authentication=ActiveDirectoryDefault;"
        "Encrypt=yes"
    )
except mssql_python.OperationalError as e:
    print(f"Connection failed: {e.driver_error}")
    # e.driver_error: "Client unable to establish connection"

连接字符串错误

连接字符串解析错误出现 ConnectionStringParseError

try:
    conn = mssql_python.connect("Servr=localhost;")  # Typo
except mssql_python.ConnectionStringParseError as e:
    print(f"Invalid connection string: {e}")
    # Output: Unknown keyword 'Servr'

SQLSTATE 代码参考

SQLSTATE 代码是五字符代码,用于识别错误状况。 前两个字符表示职业,后三个字符表示子职业。 你很少需要直接检查这些代码。 相反,要捕捉相应的 Python 异常类型(列在“异常”栏)。 当你需要区分同一例外类型内的特定错误条件时,可以使用SQLSTATE代码,例如区分死锁(40001)和一般连接故障(08S01)。

00级 - 成功完成

SQLSTATE Exception 描述
00000 没有 成功

01级 - 警告

SQLSTATE Exception 描述
01000 Warning 常规警告
01001 Warning 游标操作冲突
01002 Warning 断开错误
01003 DataError 在 set 函数中消除 NULL 值
01004 DataError 字符串数据,右截断
01006 Warning 权限未撤销
01007 Warning 未授予权限
01S00 Warning 无效连接字符串属性
01S01 Warning 行中错误
01S02 Warning 选项值已更改

07类 - 动态SQL错误

SQLSTATE Exception 描述
07001 编程错误 参数数量错误
07002 编程错误 COUNT 字段不正确
07005 编程错误 预备语句,非光标规范
07006 编程错误 受限数据类型属性冲突
07009 编程错误 无效描述子索引
07S01 编程错误 默认参数的使用无效

08类 - 连接例外

SQLSTATE Exception 描述
08001 操作错误 客户端无法建立连接
08002 操作错误 正在使用的连接名称
08003 操作错误 连接不存在
08004 操作错误 服务器拒绝了连接
08007 操作错误 交易期间的连接失败
08S01 操作错误 通信链接失败

第21类——基数违规

SQLSTATE Exception 描述
21S01 编程错误 插入值列表与列列表不匹配
21S02 编程错误 派生表的程度与列列表不匹配

类别22 - 数据异常

SQLSTATE Exception 描述
22001 DataError 字符串数据,右截断
22002 DataError 指示符变量是必需的,但未提供
22003 DataError 数值范围之外
22007 DataError 日期/时间格式无效
22008 DataError 日期/时间字段溢出
22012 DataError 除以零
22015 DataError 间隔字段溢出
22018 DataError 强制转换规范的字符值无效
22019 DataError 转义字符无效
22025 DataError 无效转义序列
22026 DataError 字符串数据,长度不匹配

第23类 - 完整性约束违规

SQLSTATE Exception 描述
23000 完整性错误 完整性约束违规(一般)

类别24 - 无效光标状态

SQLSTATE Exception 描述
24000 内部错误 游标状态无效

第25类 - 无效交易状态

SQLSTATE Exception 描述
25000 操作错误 无效交易状态
25S01 操作错误 交易状态未知
25S02 操作错误 交易仍然有效
25S03 操作错误 交易被回滚

第28类 - 无效授权规范

SQLSTATE Exception 描述
28000 操作错误 授权规范无效(登录失败)

类别34 - 光标名称无效

SQLSTATE Exception 描述
34000 编程错误 无效的游标名称

3C 类 - 重复光标名称

SQLSTATE Exception 描述
3C000 编程错误 重复的游标名称

3D类 - 目录名称无效

SQLSTATE Exception 描述
3D000 编程错误 无效的目录名称

3F类 - 模式名称无效

SQLSTATE Exception 描述
3F000 编程错误 无效的架构名称

40类 - 事务回滚

SQLSTATE Exception 描述
40001 操作错误 串行化失败(死锁)
40002 操作错误 完整性约束违规导致回滚
40003 操作错误 语句完成未知

42类 - 语法错误或访问规则违规

SQLSTATE Exception 描述
42000 编程错误 语法错误或访问冲突
42S01 编程错误 基表或视图已存在
42S02 编程错误 找不到基表或视图
42S11 编程错误 索引已存在
42S12 编程错误 找不到索引
42S21 编程错误 列已存在
42S22 编程错误 找不到列

44类——带有检查选项违规

SQLSTATE Exception 描述
44000 完整性错误 WITH CHECK OPTION 冲突

HY类——CLI特异性疾病

SQLSTATE Exception 描述
HY000 DatabaseError 常规错误
HY001 操作错误 内存分配错误
HY003 编程错误 应用程序缓冲区类型无效
HY004 编程错误 SQL 数据类型无效
HY007 编程错误 未准备好关联的语句
HY008 操作错误 操作已取消
HY009 编程错误 无效使用 null 指针
HY010 编程错误 函数序列错误
HY011 编程错误 无法立即设置属性
HY012 编程错误 无效的交易操作代码
HY013 操作错误 内存管理错误
HY014 操作错误 把手数量限制超过
HY015 编程错误 没有可用的游标名称
HY016 编程错误 无法修改实现行描述符
HY017 编程错误 自动分配描述符句柄的无效使用
HY018 操作错误 服务器拒绝了取消请求
HY019 编程错误 非字符和非二元数据分段发送
HY020 DataError 尝试连接空值
HY021 编程错误 描述符信息不一致
HY024 编程错误 属性值无效
HY090 编程错误 字符串或缓冲区长度无效
HY091 编程错误 无效描述符字段标识符
HY092 编程错误 无效属性/选项标识符
HY095 编程错误 功能类型超出范围
HY096 编程错误 无效信息类型
HY097 编程错误 列型超出范围
HY098 编程错误 望远镜类型超出范围
HY099 编程错误 可消除类型超出范围
HY100 编程错误 范围外的唯一性选项类型
HY101 编程错误 准确度选项类型范围不足
HY103 编程错误 无效检索码
HY104 编程错误 精度或小数位数值无效
HY105 编程错误 参数类型无效
HY106 编程错误 取物类型超出范围
HY107 编程错误 行值超出范围
HY109 编程错误 光标位置无效
HY110 编程错误 无效驱动补全
HY111 编程错误 无效书签值
HYC00 NotSupportedError 未实现可选功能
HYT00 操作错误 已超时
HYT01 操作错误 超过连接超时时间

类别 IM - 司机管理员错误

SQLSTATE Exception 描述
IM001 接口错误 驱动程序不支持此函数
IM002 接口错误 未找到数据源名称
IM003 接口错误 无法加载指定的驱动程序
IM004 接口错误 驱动程序的 SQLAllocHandle 在 SQL_HANDLE_ENV 失败
IM005 接口错误 驱动程序的 SQLAllocHandle 在 SQL_HANDLE_DBC 失败
IM006 接口错误 Driver's SQLSetConnectAttr failed
IM007 接口错误 未指定数据源或驱动
IM008 接口错误 对话失败
IM009 接口错误 无法加载翻译 DLL
IM010 接口错误 数据源名称太长
IM011 接口错误 驱动程序名称太长
IM012 接口错误 DRIVER 关键字语法错误
IM014 接口错误 无效DSN
IM015 接口错误 文件数据源损坏

常见的SQL Server错误编号

除了 SQLSTATE,SQL Server 还在括号内提供原生错误编号。 这些是你在应用代码中最容易遇到的错误。 围绕错误1205(死锁)和瞬态连接错误(参见 重试逻辑)构建重试逻辑。

错误 消息模式 解决方案
208 对象名称无效 确认该表或视图的存在,并检查模式资格。
547 约束冲突 外键或校验约束失败。
2627 唯一约束违背 插入了一个重复的键值。
2601 唯一索引违规 索引中存在一个重复键。
4060 无法打开数据库 数据库不存在,或者访问被拒绝。
18456 登录失败 身份验证失败。 核查资质。
1205 死锁受害者 该事务已回滚。 重试操作。

症状到异常快速引用

请使用此表将常见症状映射到你应捕捉的异常类型:

症状 Exception 可能的原因
“用户登录失败” OperationalError 错误的凭证或用户没有映射到数据库。
“客户无法建立连接” OperationalError 服务器无法访问,防火墙或DNS问题。
“暂停结束了” OperationalError 查询或连接超时。 增加超时或优化查询。
“无效对象名称” ProgrammingError 表格不存在,模式也没有指定。
“语法错误” ProgrammingError SQL 语法错误。 在SSMS中测试查询。
“参数数量错误” ProgrammingError 参数数量和占位符不匹配。
“主密钥违规” IntegrityError 复制密钥。 MERGE使用或检查后再插入。
“外来密钥的违规” IntegrityError 引用的行不存在。 先插入父级。
“交易陷入僵局” OperationalError (错误1205) 锁的争夺。 实现重试逻辑。
“字符串或二进制数据会被截断” DataError 值超过列长。 检查数据或增加列大小。
“皈依失败” DataError 类型不匹配。 使用正确的Python类型来表示列。
“未知关键词” ConnectionStringParseError 连接字符串 关键字中的拼写错误。
“Callproc不支持” NotSupportedError 改用 cursor.execute("EXECUTE ...")

最佳做法

  • 在处理泛泛的例外之前,先发现具体的例外情况。 按最具体的(IntegrityError)到最不具体Error的()排序。
  • 数据修改操作时务必处理IntegrityError 。 约束违规在正常操作中是预期的(例如,用户试图创建重复的用户名)。
  • 记录完整的错误上下文 以便排查。 该例外暴露 driver_error (稳定文本,SQLSTATE派生文本)和 ddbc_error (服务器端消息)。 两者都记录;分类。driver_error
  • 为瞬态错误(连接失败、死锁)实现重试逻辑。 参见 重试逻辑
  • 在例外处理程序中使用rollback()来清理失败的事务。 如果没有显式回滚,连接会保持交易失败状态。