在使用 mssql-python 驅動程式連接 SQL Server、Azure SQL Database、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 入口網站的 Azure SQL 防火牆規則中加入你的客戶端 IP。
- 若要連線到 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 Database、Azure SQL 受控執行個體 以及 Fabric 中的 SQL 資料庫,建議使用 Microsoft Entra 模式,例如
Authentication=ActiveDirectoryDefault。 - 如果你是刻意使用 SQL 認證,請確認伺服器是否允許,且你使用的登入格式是否正確。
- 對於 Azure SQL Database、Azure SQL 受控執行個體 以及 Fabric 中的 SQL 資料庫,建議使用 Microsoft Entra 模式,例如
錯誤的 SQL 認證憑證
- 請驗證使用者名稱和密碼。
- 對於 Azure SQL,請包含完整使用者名稱:
username@servername。
使用者不存在於資料庫中
- 確認使用者是否能存取指定的資料庫。
- 檢查登入是否對應到資料庫使用者。
認證未設定
- 建議使用 Microsoft Entra 認證:
Authentication=ActiveDirectoryDefault。 - 如果你正在排除本機 SQL Server 應該接受 SQL 驗證的故障,請確認 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
)
Caution
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
交易未承諾
症狀:
關閉連線後,資料變更不會持續存在。
Solution:
當 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
Solution:
重試邏輯(參見 重試邏輯)負責立即失敗,但反覆出現的死結表示設計有問題。 為了解決根本原因,請擷取死結圖並分析涉及哪些語句與鎖類型。 常見的修正包括重新排序操作,使競爭交易能以相同順序取得鎖、縮小交易範圍,以及新增適當的索引以縮短鎖定時間。
欲了解完整的死結分析,請參閱 死結指南。 如果你正在使用 Azure SQL Database,請參閱「分析與防止死結」。
散裝載重問題
批量複製期間的限制違規
症狀:
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()
關於帶有暫存表的上繼模式,請參見 資料載入與移動模式。
欄位映射錯誤
症狀:
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 的值不會乾淨俐落地對應到目標欄位類型。 常見情況包括:將值載入 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 整數或浮點數型別時,會默默地失效或引發資料型別錯誤。
原因:
像是 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)
容器與持續整合問題
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 格式寫入,並在達到 512 MB 時自動輪替,保留五個備份檔。 像密碼和存取權杖這類敏感資料會在日誌輸出中自動淨化。
若要在駕駛員日誌旁邊新增您自己的日誌條目,請使用 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
Caution
記錄日誌會產生效能負擔。 只在故障排除時啟用,不要預設在生產環境啟用。
取得駕駛資訊
從活躍連線中取得驅動程式版本及伺服器資料:
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 | 鎖定爭用 | 重試,然後 分析死結圖 |