mssql-python 的資料型別映射

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 整合 路徑,這些路徑內部處理型別轉換。