在使用 mssql-python 驱动连接 SQL Server、Azure SQL 数据库、Azure SQL 托管实例 和 Microsoft Fabric 中的 SQL 数据库时,诊断并解决常见问题。
安装问题
PIP安装失败或从源代码构建
症状:
error: Microsoft Visual C++ 14.0 or greater is required
ERROR: Failed building wheel for mssql-python
可能的原因和解决方案:
没有适合你平台的预装轮毂
虚拟环境未激活
- 先激活你的虚拟环境。 安装到 Python 系统中可能会导致权限错误或冲突。
python -m venv .venv .venv\Scripts\activate pip install mssql-python
-
缺失的Linux系统库
- 该驱动程序在 Linux 上需要一小部分系统库。 关于安装包的具体依赖,请参见 平台特定依赖 。
冲突的驱动程序安装
症状:
在同一环境中同时安装 mssql-python 和 pyodbc 后,出现导入错误或异常行为。
修复:
mssql-python 和 pyodbc 可以共存。 如果你发现冲突,创建一个干净的虚拟环境:
python -m venv .venv --clear
.venv\Scripts\activate
pip install mssql-python
连接问题
无法连接到服务器
症状:
OperationalError: [08001] (0) Client unable to establish connection
可能的原因和解决方案:
服务器无法访问
- 请确认服务器名称和端口是否正确。
- 检查网络连接:
ping servername或telnet servername 1433。 - 确保防火墙允许端口1433的出站连接。
SQL Server 无法运行
- 请确认 SQL Server 服务已启动。
- 对于命名实例,请确认 SQL Server 浏览器服务是否在运行。
Azure SQL firewall rules
- 在 Azure 门户中将你的客户端 IP 添加到 Azure SQL 防火墙规则中。
- 对于 Azure SQL 托管实例,请确保是从允许的网络进行连接。
# Test basic connectivity
import socket
try:
sock = socket.create_connection(("<server>.database.windows.net", 1433), timeout=5)
print("TCP connection successful")
sock.close()
except Exception as e:
print(f"Cannot reach server: {e}")
登录失败
症状:
OperationalError: [28000] (18456) Login failed for user 'username'.
可能的原因和解决方案:
认证模式不匹配
- 对于 Azure SQL 数据库、Azure SQL 托管实例 和 Fabric 中的 SQL 数据库,优先使用 Microsoft Entra 模式,例如
Authentication=ActiveDirectoryDefault。 - 如果你有意使用 SQL 认证,请确认服务器是否允许,并且你使用的登录格式是正确的。
- 对于 Azure SQL 数据库、Azure SQL 托管实例 和 Fabric 中的 SQL 数据库,优先使用 Microsoft Entra 模式,例如
错误的SQL认证凭证
- 核实用户名和密码。
- 对于 Azure SQL,请包含完整用户名:
username@servername。
数据库中不存在用户
- 确认用户是否访问了指定的数据库。
- 检查登录是否映射到数据库用户。
认证未配置
- 使用 Microsoft Entra 认证(推荐):
Authentication=ActiveDirectoryDefault。 - 如果你正在排查本应接受 SQL 身份验证的本地 SQL Server,请确认 SQL Server 是否使用混合模式身份验证。
- 使用 Microsoft Entra 认证(推荐):
连接超时
症状:
OperationalError: [HYT00] (0) Timeout expired
OperationalError: [HYT01] (0) Connection timeout expired
可能的原因和解决方案:
服务器响应缓慢
- 增加连接超时时间:
conn = mssql_python.connect(connection_string, timeout=60)网络延迟
- 检查服务器的网络路径。
- 考虑使用更短的网络路径或VPN。
服务器负载过重
- 试着在非高峰时段连接。
- 请联系你的数据库管理员。
SSL 证书错误
症状:
OperationalError: [08001] SSL Provider: The certificate chain was issued by an authority that is not trusted
解决方案:
首先,优先选择可信证书或容器 和本地开发中的本地开发模式。 仅应将 TrustServerCertificate=yes 用于针对你所控制的服务器进行本地开发。
使用自签名证书进行开发和测试:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
"TrustServerCertificate=yes;" # Don't use in production
)
注意
TrustServerCertificate=yes 是仅限本地的备选方案。 不要把它带进共享开发容器、CI流水线或生产部署。 更广泛的指导请参见 加密与证书。
在生产过程中,确保安装了正确的证书并使用:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
"HostnameInCertificate=<server>.domain.com;"
)
查询执行问题
未找到表格或对象
症状:
ProgrammingError: [42S02] (208) Invalid object name 'TableName'.
可能的原因和解决方案:
错误的数据库上下文
# Ensure you're connected to the correct database cursor.execute("SELECT DB_NAME()") print(cursor.fetchone()[0])模式未指定
# Use fully qualified name cursor.execute("SELECT * FROM dbo.TableName")表格不存在
# Check if table exists cursor.execute(""" SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'TableName' """)
语法错误
症状:
ProgrammingError: [42000] (102) Incorrect syntax near '...'.
解决方案:
先在SSMS中测试SQL 以验证语法
检查字符串转义:使用参数化查询:
# Wrong - vulnerable to syntax issues and SQL injection cursor.execute(f"SELECT * FROM Production.Product WHERE Name = '{name}'") # Correct - use parameters cursor.execute("SELECT * FROM Production.Product WHERE Name = %(name)s", {"name": name})
参数误差
症状:
ProgrammingError: [07001] Wrong number of parameters
解决方案:
占位符和参数的数量——二者必须一致
选择合适的参数样式:
# Qmark style - positional cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Name LIKE ?", (1, "Adjustable%")) print(cursor.fetchone()) # Pyformat style - named cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(id)s AND Name LIKE %(name)s", {"id": 1, "name": "Adjustable%"}) print(cursor.fetchone())
数据类型问题
日期时间转换错误
症状:
DataError: [22007] Invalid datetime format
解决方案:
使用Python的datetime对象代替字符串:
from datetime import datetime
cursor.execute("CREATE TABLE #Events (EventDate DATETIME)")
# Wrong - this raises an error for invalid dates
try:
cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": "2024-13-45"})
except Exception as e:
print(f"Expected error: {e}")
# Correct - use Python datetime objects
cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": datetime(2024, 3, 15)})
cursor.execute("SELECT EventDate FROM #Events")
print(cursor.fetchone())
十进制精度问题
症状:
数字似乎被截断或四舍五入。
解决方案:
使用 decimal.Decimal 表示精确的数值:
from decimal import Decimal
cursor.execute("CREATE TABLE #PriceDemo (ListPrice DECIMAL(10,2))")
# Preserve full precision
cursor.execute(
"INSERT INTO #PriceDemo (ListPrice) VALUES (%(list_price)s)",
{"list_price": Decimal("19.99")}
)
Unicode 编码问题
症状:
特殊字符会显得杂乱或导致错误。
解决方案:
在数据库中使用 NVARCHAR 列 来存储 Unicode 数据
直接传递字符串 ——驱动程序负责编码:
cursor.execute("CREATE TABLE #UnicodeDemo (Name NVARCHAR(50))") cursor.execute("INSERT INTO #UnicodeDemo (Name) VALUES (%(name)s)", {"name": "日本語"}) cursor.execute("SELECT Name FROM #UnicodeDemo") print(cursor.fetchone())
性能问题
查询执行缓慢
可能的原因和解决方案:
缺少索引:检查SSMS中的查询执行计划。
大型结果集:使用
fetchmany()代替fetchall():cursor.arraysize = 1000 while True: rows = cursor.fetchmany() if not rows: break process_rows(rows)禁用连接池:启用串池:
import mssql_python mssql_python.pooling(max_size=20, idle_timeout=300)
大型结果集的内存问题
症状:
Python进程内存耗尽。
解决方案:
以流式方式返回结果,而不是将所有结果都加载到内存中:
cursor.execute("SELECT * FROM LargeTable") for row in cursor: # Iterates one row at a time process_row(row)使用服务器端分页:
page_size = 1000 offset = 0 while True: cursor.execute( "SELECT * FROM LargeTable ORDER BY ID " "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY", (offset, page_size) ) rows = cursor.fetchall() if not rows: break process_rows(rows) offset += page_size
交易问题
自动提交模式下的临时表作用域
在事务中创建的临时表(#tablename)在事务回滚时会消失。 当自动提交关闭(默认设置)时,这常常引起混淆:
conn = mssql_python.connect(connection_string) # autocommit=False by default
cursor = conn.cursor()
cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
# If the connection rolls back (explicit or on error), #TempData disappears
conn.rollback()
# This fails: Invalid object name '#TempData'
cursor.execute("SELECT * FROM #TempData")
修复方法: 创建临时表后立即提交,或使用自动提交模式:
cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
conn.commit() # Lock in the table definition
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
conn.commit()
需要自动提交模式的DDL语句,如 CREATE DATABASE,在未完成的事务中会失败。 在运行它们之前设置自动提交:
conn.autocommit = True
cursor.execute("CREATE DATABASE TestDB")
conn.autocommit = False
交易未提交
症状:
关闭连接后,数据更改不会持续存在。
解决方案:
使用 autocommit=False(默认值)时,你必须调用 commit():
cursor.execute("CREATE TABLE #Products (Name NVARCHAR(100))")
cursor.execute("INSERT INTO #Products (Name) VALUES (%(name)s)", {"name": "Widget"})
conn.commit() # Don't forget this!
或者使用自动提交模式:
conn = mssql_python.connect(connection_string, autocommit=True)
死锁错误
症状:
OperationalError: [40001] (1205) Transaction ... was deadlocked on lock resources with another process
解决方案:
重试逻辑(参见 重试逻辑)处理即时失败,但反复出现的死锁表明设计存在问题。 为了解决根本原因,捕捉死锁图并分析涉及的语句和锁类型。 常见的修复方法包括重新排序操作,使竞争事务以相同顺序获得锁,缩小事务范围,以及添加适当的索引以缩短锁的持续时间。
关于死锁分析的完整攻略,请参见 死锁指南。 如果你正在使用 Azure SQL 数据库,请参见“分析并防止死锁”。
批量加载问题
批量复制期间的约束违规
症状:
RuntimeError: CHECK constraint ... Conflict occurred in database ...
RuntimeError: Cannot insert duplicate key ... violation of PRIMARY KEY constraint
原因:
你的批处理中的数据违反了表约束(主键、唯一键、CHECK或外键)。
修复:
加载前验证数据。 对于大型数据集,请先加载到暂存表中,然后再合并到目标表中:
# Load into staging, then validate
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(100))")
cursor.bulkcopy("##Staging", rows)
# Check for duplicates before merging
cursor.execute("""
SELECT s.ID FROM ##Staging s
INNER JOIN dbo.Target t ON s.ID = t.ID
""")
dupes = cursor.fetchall()
if dupes:
print(f"Skipping {len(dupes)} duplicate rows")
# Insert only non-duplicate rows
cursor.execute("""
INSERT INTO dbo.Target (ID, Name)
SELECT s.ID, s.Name FROM ##Staging s
WHERE NOT EXISTS (SELECT 1 FROM dbo.Target t WHERE t.ID = s.ID)
""")
conn.commit()
关于带有预留表的upsert模式,请参见 数据加载和移动模式。
列映射错误
症状:
RuntimeError: Bulk copy failure - column count mismatch
原因:
你数据中的列数和目标表的列数不匹配,或者列的顺序错误。
修复:
确保你的数据在顺序和计数上完全符合表模式:
# Check the target table schema
cursor.execute("""
SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'MyTable'
ORDER BY ORDINAL_POSITION
""")
for col in cursor.fetchall():
print(col)
# Match your data to the column order
rows = [
(1, "Widget", Decimal("19.99")), # Must match table column order
(2, "Gadget", Decimal("29.99")),
]
cursor.bulkcopy("dbo.MyTable", rows)
批量复制时出现类型不匹配
症状:
数据加载时,值被截断、四舍五入或错误。
原因:
Python 的值并不能干净地映射到目标列类型。 常见情况:将 float 值加载到 decimal 列中(精度丢失),或将过长字符串加载到定长列中。
修复:
使用与你模式匹配的正确 Python 类型:
from decimal import Decimal
# Use Decimal for decimal/numeric columns, not float
rows = [
(1, "Widget", Decimal("19.99")), # Correct
# (1, "Widget", 19.99), # Avoid: float loses precision
]
cursor.bulkcopy("dbo.Products", rows)
NumPy 类型绑定失败
症状:
参数在使用numpy整数或float类型时会默默失败或引发数据类型错误。
原因:
像 numpy.int64 和 numpy.int32 这样的 NumPy 类型在 NumPy 2.x 中无法通过 isinstance(x, int)。 驱动程序的类型推断无法识别它们,导致意外行为。
修复:
绑定前将 numpy 值转换为原生 Python 类型:
import numpy as np
# Convert individual values
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(product_id)s", {"product_id": int(np.int64(42))})
# Convert DataFrame values
for _, row in df.iterrows():
cursor.execute(
"INSERT INTO #Orders (ProductID, Qty) VALUES (%(product_id)s, %(qty)s)",
{"product_id": int(row["ProductID"]), "qty": int(row["Qty"])}
)
对于较大的数据集,可以使用 Arrow 或 pandas 集成路径,它们内部处理类型转换。
使用临时表的批量复制
症状:
cursor.bulkcopy("#TempTable", data) 引发了 RuntimeError: Invalid object name '#TempTable'。
原因:
bulkcopy() 由于元数据查找的限制,无法解析会话临时表(#tablename)。 全局温度表(##tablename)和永久表都有效。
修复:
可以使用全局温度表或常规预留表:
# Global temp table (visible to all sessions, dropped when last session disconnects)
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("##Staging", rows)
# Or use a permanent staging table
cursor.execute("CREATE TABLE dbo.Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("dbo.Staging", rows)
对于偏好使用会话临时表的小数据集,请改用 executemany() :
cursor.execute("CREATE TABLE #Staging (ID INT, Name NVARCHAR(50))")
cursor.executemany("INSERT INTO #Staging (ID, Name) VALUES (?, ?)", rows)
容器和CI问题
Linux 上缺失的系统库
症状:
ImportError: libltdl.so.7: cannot open shared object file: No such file or directory
ImportError: libkrb5.so.3: cannot open shared object file
修复:
安装所需的系统包。 各套餐因发行版而异:
| Distribution | 安装命令 |
|---|---|
| Ubuntu/Debian | sudo apt-get install libltdl7 libkrb5-3 libgssapi-krb5-2 |
| 红帽 / Fedora | sudo dnf install libtool-ltdl krb5-libs |
| Alpine | apk add libltdl krb5-libs |
关于 Dockerfile 示例,请参见 容器与本地开发。
macOS 安装后出现的 SSL 错误
症状:
从macOS连接时,尤其是在Apple Silicon上,会出现与SSL相关的错误。
修复:
通过Homebrew安装OpenSSL,并设置链接标志:
brew install openssl
export LDFLAGS="-L/opt/homebrew/opt/openssl/lib"
export CPPFLAGS="-I/opt/homebrew/opt/openssl/include"
诊断工具
启用驱动程序日志
使用 mssql_python.setup_logging() 启用全面的 DEBUG 日志记录,以便进行故障排查。 所有驱动程序操作都会被记录,包括SQL语句、参数、内部ODBC操作和连接状态变化。
import mssql_python
# Enable logging to file (default)
mssql_python.setup_logging()
# Output to stdout (useful for CI/CD and containers)
mssql_python.setup_logging(output='stdout')
# Output to both file and stdout
mssql_python.setup_logging(output='both')
# Custom log file path (must use .txt, .log, or .csv extension)
mssql_python.setup_logging(log_file_path="/var/log/myapp/mssql.log")
日志文件以CSV格式编写,自动旋转,大小为512MB,并有五个备份。 像密码和访问令牌这样的敏感数据会在日志输出中自动净化。
要在驾驶日志旁边添加您自己的日志条目,请使用 driver_logger:
from mssql_python.logging import driver_logger
mssql_python.setup_logging()
driver_logger.debug("[App] Starting data processing")
driver_logger.error("[App] Failed to process record")
# Your entries appear in the same file with the same format
注意
日志记录会带来性能开销。 只在排查故障时启用,默认不要在生产环境中启用。
获取驾驶员信息
从活跃连接中获取驱动版本和服务器详细信息:
import mssql_python
conn = mssql_python.connect(connection_string)
# Driver version
print(f"Version: {mssql_python.__version__}")
# Server information
print(f"Server name: {conn.getinfo(mssql_python.SQL_SERVER_NAME)}")
print(f"Database name: {conn.getinfo(mssql_python.SQL_DATABASE_NAME)}")
检查连接状态
在尝试操作前,先测试连接是否仍然开放:
try:
cursor = conn.cursor()
cursor.execute("SELECT 1")
print("Connection is open")
except mssql_python.Error:
print("Connection is closed or broken")
快速参考:常见错误
| 错误 | SQLSTATE | 常见原因 | 快速修复 |
|---|---|---|---|
| 客户端无法建立连接 | 08001 | 服务器无法访问 | 检查服务器名称/端口 |
| 登录失败 | 28000 | 凭据错误 | 验证用户名/密码 |
| 已超时 | HYT00/HYT01 | 网络速度缓慢 | 延长超时时间 |
| 对象名称无效 | 42S02 | 错误的表/模式 | 使用完全限定的名称 |
| 语法错误 | 42000 | SQL 错误 | 使用参数化查询 |
| 违反约束 | 23000 | FK/PK违规 | 检查数据完整性 |
| 死锁 | 40001 | 锁争用 | 重试,然后 分析死锁图 |