mssql-python 驅動程式在傳送參數時會自動將 Python 資料型態對應到 SQL Server 型別,並在取得結果時將 SQL Server 型別轉換成 Python 型別。
Python 到 SQL Server 映射
當你將 Python 值作為參數傳入 execute() 或 executemany()時,驅動程式會自動選擇適當的 SQL Server 類型。 對大多數應用來說,自動映射是正確的。 只有在需要覆寫預設值時才使用 setinputsizes() (稍後會說明),例如強制用 varchar 取代 nvarchar ,或控制字串和整數輸入的參數元資料。
| Python 類型 | SQL Server 類型 | Notes |
|---|---|---|
None |
NULL |
SQL NULL 值。 |
bool |
位元 |
True→1, False→0。 |
int |
微型、小型、型、型 | 尺寸選擇依據價值範圍。 |
float |
float | 64 位元浮點運算。 |
decimal.Decimal |
十進位、 數字 | 可維持最高 38 位數的精確度。 |
str |
瓦爾查爾, 恩瓦爾查爾 | 使用 nvarchar 來處理 Unicode 內容。 |
bytes、bytearray |
varbinary | 二進位資料。 |
datetime.date |
date | 只限日期。 |
datetime.time |
time | 只有時間。 |
datetime.datetime |
datetime2 | 以微秒級的精確度記錄日期和時間。 |
uuid.UUID |
uniqueidentifier | 16 位元組的 GUID。 |
這些映射描述了參數化的執行。 使用元組批量複製,ISO 格式字串可保留 datetime2(7) 的七個分秒數字。 對於 datetimeoffset(7) 字串,保留分數精度,但偏移量則正規化至 UTC。 時間字串(7)會透過 Python datetime.time 解析,並去除第七位小數。 Python datetime 和time物件只攜帶六個分秒的數字。 欲了解更多資訊,請參閱日期處理。
整數型選擇
驅動程式會自動選擇能儲存該值的最小整數類型:
| 值範圍 | SQL Server 類型 |
|---|---|
| 0 到 255 | tinyint |
| -32,768 至 32,767 | smallint |
| -2,147,483,648 至 2,147,483,647 | int |
| 較大的數值 | bigint |
大值類型
對於超出標準限制的字串與二進位資料,驅動程式會自動使用 MAX 類型:
| 狀況 | SQL Server 類型 |
|---|---|
| 字串 > 8,000 位元組(varchar) | varchar(max) |
| 字串 > 4,000 字條(nvarchar) | nvarchar(max) |
| 二進位 > 8,000 位元組 | varbinary(max) |
驅動程式會將大量值串流給伺服器,以減少記憶體使用。
SQL Server 到 Python 映射
當驅動程式從 SQL Server 取得資料時,會將值轉換成 Python 類型:
| SQL Server 類型 | Python 類型 | Notes |
|---|---|---|
| 位元 | bool |
正確/錯誤。 |
| 微型、小型、型、型 | int |
Python整數(任意精度)。 |
| real | float |
32 位浮點數。 |
| float | float |
64 位元浮點運算。 |
| 十進位、 數字 | decimal.Decimal |
保持精確度。 |
| 錢, 小錢 | decimal.Decimal |
固定精度(小數點四位)。 |
| char, varchar, 文本 | str |
解碼為 UTF-8。 |
| 恩查爾, 恩瓦爾查爾, ntext | str |
解碼為 UTF-16LE。 |
| binary、varbinary、image | bytes |
原始二元進位。 |
| date | datetime.date |
只限日期。 |
| time | datetime.time |
時間精確到微秒級。 |
| 約會時間, 小約會時間 | datetime.datetime |
傳統的約會型。 |
| datetime2 | datetime.datetime |
高精度約會時間。 |
| datetimeoffset | datetime.datetime |
時區感知日期時間。 |
| uniqueidentifier | uuid.UUID |
Python UUID 物件。 |
| xml | str |
XML 作為文字。 |
| json | str |
JSON 文件以文字形式呈現。 |
| vector | str |
向量作為 JSON 陣列字串。 |
| 地理、 幾何 | bytes |
空間類型是二元的。 |
| 層次識別碼(hierarchyid) | bytes |
階層資料以二進位形式呈現。 |
| sql_variant | 情況不一 | 解析為底層基底類型。
sql_variant 欄位使用串流擷取路徑,這可能會比固定型欄位稍微影響效能。 |
NULL |
None |
Python 沒有。 |
SQL 型別常數
驅動程式匯出對應 ODBC SQL 類型識別碼的常數。 當自動映射與你的結構不符時,通常會用這些常數 setinputsizes() 來覆寫驅動程式預設的類型推論。
字元類型
| 恆定 | 價值 | 描述 |
|---|---|---|
SQL_CHAR |
1 | 固定長度 ANSI 字元。 |
SQL_VARCHAR |
12 | 可變長度 ANSI 字元。 |
SQL_LONGVARCHAR |
-1 | 長型可變長度ANSI。 |
SQL_WCHAR |
-8 | 固定長度 Unicode。 |
SQL_WVARCHAR |
-9 | 可變長度 Unicode。 |
SQL_WLONGVARCHAR |
-10 | 長可變長度的 Unicode。 |
數值類型
| 恆定 | 價值 | 描述 |
|---|---|---|
SQL_BIT |
-7 | 位元/布林值。 |
SQL_TINYINT |
-6 | 8 位無符號整數。 |
SQL_SMALLINT |
5 | 16 位帶正負號的整數。 |
SQL_INTEGER |
4 | 32位元有號整數。 |
SQL_BIGINT |
-5 | 64 位元有號整數。 |
SQL_REAL |
7 | 32 位浮點數。 |
SQL_FLOAT |
6 | 64 位元浮點運算。 |
SQL_DOUBLE |
8 | 64 位元浮點運算。 |
SQL_DECIMAL |
3 | 固定精度十進位。 |
SQL_NUMERIC |
2 | 固定精度數值。 |
日期和時間類型
| 恆定 | 價值 | 描述 |
|---|---|---|
SQL_TYPE_DATE |
91 | 只限日期。 |
SQL_TYPE_TIME |
92 | 只有時間。 |
SQL_TYPE_TIMESTAMP |
93 | 日期和時間。 |
SQL_SS_TIME2 |
-154 | SQL Server time(n). |
SQL_DATETIMEOFFSET |
-155 | SQL Server datetimeoffset. |
Note
SQL_SS_TIME2 和 SQL_DATETIMEOFFSET 是內部常數,非匯出為模組層級屬性。 呼叫 -154時直接使用整數值 (-155, add_output_converter())
二元類型
| 恆定 | 價值 | 描述 |
|---|---|---|
SQL_BINARY |
-2 | 固定長度二進位。 |
SQL_VARBINARY |
-3 | 可變長度二進位。 |
SQL_LONGVARBINARY |
-4 | 長可變長度二進位。 |
其他類型
| 恆定 | 價值 | 描述 |
|---|---|---|
SQL_GUID |
-11 | 唯一識別碼。 |
SQL_XML |
-152 | XML 資料。 |
SQL_SS_UDT |
-151 | 使用者自訂型別(空間型)。 |
SQL_SS_VARIANT |
-150 | sql_variant數據。 |
Note
SQL_SS_UDT 和 SQL_SS_VARIANT 是內部常數,非匯出為模組層級屬性。 呼叫 -151時直接使用整數值 (-150, add_output_converter())
使用 setinputsize()
當驅動程式的自動型別推論導致意外行為時,例如setinputsizes()executemany()欄位為 但varchar(100)驅動程式傳送 nvarchar:
Note
對於小數值,請依賴驅動程式的自動型別推論,而非 SQL_DECIMAL 或 SQL_NUMERIC。 這些明確的十進位類型常數目前已知存在問題。setinputsizes() 欲了解更多資訊,請參閱 執行查詢。
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
# Declare types: (sql_type, precision, scale)
cursor.setinputsizes([
(mssql_python.SQL_WVARCHAR, 100, 0), # nvarchar(100)
(mssql_python.SQL_INTEGER, 0, 0), # int
])
cursor.executemany(
"SELECT ProductID, Name FROM Production.Product WHERE Name LIKE ? AND ProductSubcategoryID = ?",
[
("Road%", 2),
("Mountain%", 1),
]
)
小數分隔符號
驅動程式使用區域感知的十進位分隔符。 自訂方式:
import mssql_python
# Get current separator
sep = mssql_python.getDecimalSeparator()
print(f"Current separator: {sep}") # Usually "."
# Set custom separator (for locales using comma)
mssql_python.setDecimalSeparator(",")
客製化類型轉換器
針對特定 SQL 類型設置的暫存器自訂轉換器:
import mssql_python
from decimal import Decimal
def money_to_float(value):
"""Convert money values to float instead of Decimal."""
if value is None:
return None
return float(value)
conn.add_output_converter(Decimal, money_to_float)
欲了解更多資訊,請參閱 自訂類型轉換器。
特殊握把類型
UUID 處理
驅動程式自動映射 uuid.UUID 到 uniqueidentifier 。 你可以在不手動字串轉換的情況下插入和取回 UUID:
import uuid
cursor.execute("CREATE TABLE #Users (UserId UNIQUEIDENTIFIER PRIMARY KEY, Name NVARCHAR(100))")
# Insert a generated UUID
user_id = uuid.uuid4()
cursor.execute("INSERT INTO #Users (UserId, Name) VALUES (%(user_id)s, %(name)s)", {"user_id": user_id, "name": "Alice"})
conn.commit()
# Retrieve as uuid.UUID object (default behavior)
cursor.execute("SELECT UserId, Name FROM #Users WHERE Name = %(name)s", {"name": "Alice"})
row = cursor.fetchone()
print(f"Type: {type(row.UserId)}") # <class 'uuid.UUID'>
print(f"UUID: {row.UserId}") # e.g., 3b4c8f2a-...
# Use the returned UUID directly in subsequent queries
cursor.execute("SELECT Name FROM #Users WHERE UserId = %(user_id)s", {"user_id": row.UserId})
預設情況下,驅動程式會將欄位以UNIQUEIDENTIFIER物件形式回傳uuid.UUID。 若要返回與 pyodbc 相容的大寫字串,請設定 native_uuid=False:
import uuid
# Per-connection: return UUIDs as strings
conn2 = mssql_python.connect(connection_string, native_uuid=False)
cursor2 = conn2.cursor()
cursor2.execute("CREATE TABLE #UuidDemo (Id UNIQUEIDENTIFIER DEFAULT NEWID(), Label NVARCHAR(50))")
cursor2.execute("INSERT INTO #UuidDemo (Label) VALUES (%(label)s)", {"label": "test"})
conn2.commit()
cursor2.execute("SELECT Id FROM #UuidDemo")
row = cursor2.fetchone()
print(type(row[0])) # <class 'str'>
print(row[0]) # e.g., 3B4C8F2A-...
conn2.close()
關於模組層級的配置,請參見native_uuid模組配置中的設定。
日期時間與時區
datetimeoffset 欄位會回傳時區感知 datetime 的物件。 關於如何處理偏移感知值與不移量純淨值的指引,請參見 日期時間處理。
cursor.execute("SELECT SYSDATETIMEOFFSET()")
row = cursor.fetchone()
dt = row[0]
print(f"DateTime: {dt}")
print(f"Timezone: {dt.tzinfo}")
空間資料
驅動程式回傳空間類型(,geographygeometry ),為 bytes。 你可以使用參數字串的 WKT 格式:
# Insert using WKT
cursor.execute("CREATE TABLE #Locations (Name NVARCHAR(100), Geo GEOGRAPHY)")
cursor.execute(
"INSERT INTO #Locations (Name, Geo) VALUES (%(name)s, geography::STGeomFromText(%(wkt)s, 4326))",
{"name": "Seattle", "wkt": "POINT(-122.33 47.60)"}
)
# Retrieve as bytes
cursor.execute("SELECT Geo.STAsBinary() FROM #Locations")
row = cursor.fetchone()
geo_bytes = row[0]
行版本/時間戳記
SQL Server rowversion (以前稱為 timestamp)是二進位型態,而非 datetime:
cursor.execute("""
CREATE TABLE #RowVersionDemo (
ID int PRIMARY KEY,
Version rowversion
)
""")
cursor.execute("INSERT INTO #RowVersionDemo (ID) VALUES (1)")
cursor.execute("SELECT Version FROM #RowVersionDemo WHERE ID = 1")
row = cursor.fetchone()
version = row[0] # bytes, not datetime
串流大值類型
在插入或取回varchar(max)資料nvarchar(max)varbinary(max)時,驅動程式會使用執行時資料串流(DAE)將資料分段傳送,而非一次將整個值載入記憶體。
當輸入資料超過大小門檻時,串流會自動啟動:
| 資料類型 | 串流閾值 |
|---|---|
| varchar(max) | > 8,000 位元組 |
| nvarchar(max) | > 4,000字元 |
| varbinary(max) | > 8,000 位元組 |
串流支援 execute()、 executemany(),以及所有取用 API(fetchone(), fetchmany(), fetchall())。 驅動程式不需要特殊設定,且能自動處理大數值的串流。
不支援的 SQL Server 類型
以下 SQL Server 類型沒有原生的 Python 類型映射。
| SQL Server 類型 | 狀態 |
|---|---|
| table | 不支援(以表格值為單位的參數) |
Tip
驅動程式回傳 json ,並將 vector 值為 str。 在沒有原生json型別的伺服器上,將 JSON 資料儲存在nvarchar(max)欄位中,並以 SQL Server 的 JSON 函式(JSON_VALUE, JSON_QUERY, , OPENJSON) 查詢。 關於模式與範例,請參見 JSON 資料。
以下類型會回傳資料,bytes但沒有原生的 Python 型態映射:
| SQL Server 類型 | Python 類型 | Notes |
|---|---|---|
| geography | bytes |
用於 .STAsBinary() WKB 格式。 |
| geometry | bytes |
用於 .STAsBinary() WKB 格式。 |
| 層次識別碼(hierarchyid) | bytes |
二元代表。 |
| sql_variant | 情況不一 | 解析為底層基底類型。 |
十進位與浮點數精確度
當精確度很重要時,例如 decimal.Decimal 財務計算、貨幣,或任何無法四捨五入誤差的數值。 當近似值可接受時使用 float ,例如科學測量或感測器讀數。
驅動程式對應decimal.Decimal至 SQL Serverdecimal/numeric,並保留最高 38 位數的精確度。
float映射到 SQL Server float (64 位元 IEEE 754),這可能會引入捨入的產物:
from decimal import Decimal
# Exact - use for currency and financial data
price = Decimal("19.99")
tax_rate = Decimal("0.0825")
total = price * (1 + tax_rate) # Decimal("21.6391750")
cursor.execute("""
CREATE TABLE #PrecisionDemo (
Description NVARCHAR(100),
DiscountPct DECIMAL(5,2),
Rating FLOAT
)
""")
cursor.execute(
"INSERT INTO #PrecisionDemo (Description, DiscountPct, Rating) VALUES (%(desc)s, %(pct)s, %(rating)s)",
{"desc": "Summer sale", "pct": Decimal("0.15"), "rating": 4.5}
)
cursor.execute("SELECT DiscountPct, Rating FROM #PrecisionDemo WHERE Description = %(desc)s", {"desc": "Summer sale"})
row = cursor.fetchone()
print(type(row.DiscountPct)) # <class 'decimal.Decimal'>
print(type(row.Rating)) # <class 'float'>
在擷取 decimal/numeric 欄位時,驅動程式總是回傳 decimal.Decimal 物件。 當擷取float/real欄位時,驅動程式會回傳 Python 。 float
Warning
不要為了平等而比較 float 價值。 改用公差範圍: abs(a - b) < 0.0001。
numpy 型裝訂
驅動程式 mssql-python 會用 isinstance() 檢查來決定參數的 SQL 類型。 numpy 整數類型(,numpy.int64numpy.int32 , numpy.int8)在 NumPy 2.x 中無法通過isinstance(x, int),導致綁定失敗。 之所以numpy.float64能直接運作,是因為它是 Python float的子類別。
在將 numpy 值作為參數傳遞前,先將 numpy 值轉換成原生 Python 類型:
import numpy as np
row_id = np.int64(42)
score = np.float32(3.14)
# This fails: numpy.int64 is not recognized as int
# cursor.execute("SELECT * FROM Products WHERE ID = ?", (row_id,))
# Convert to native Python types first
cursor.execute(
"SELECT * FROM Production.Product WHERE ProductID = %(product_id)s",
{"product_id": int(row_id)}
)
# For DataFrames, convert the whole column
import pandas as pd
df = pd.DataFrame({"ProductID": [1, 2, 3], "Quantity": [10, 20, 30]})
cursor.execute("CREATE TABLE #Orders (ProductID INT, Quantity INT)")
for _, row in df.iterrows():
cursor.execute(
"INSERT INTO #Orders (ProductID, Quantity) VALUES (%(product_id)s, %(quantity)s)",
{"product_id": int(row["ProductID"]), "quantity": int(row["Quantity"])}
)
如果你經常使用 pandas 或 numpy 資料,可以考慮使用 Arrow 整合 或 PANDAS 整合 路徑,這些路徑內部處理型別轉換。