Microsoft SQL 提供多種字串類型,mssql-python 驅動程式會將這些字串映射到 Python str 物件。 關鍵決定是使用 varchar (非 Unicode)還是 nvarchar (Unicode):
- 當您的資料可能包含非 ASCII 字元時,請使用
nvarchar,例如姓名、地址或任何語言的使用者產生內容。 - 當資料是嚴格的 ASCII(代碼、識別碼、電子郵件地址)且你想節省儲存空間時,才會使用
varchar。varchar每個字元使用 1 位元組;nvarchar每個字元使用 2 位元組。
| SQL 型別 | Unicode | 最大長度 | Python 類型 |
|---|---|---|---|
char(n) |
No | 8,000 | str |
varchar(n) |
No | 8,000 | str |
varchar(max) |
No | 2 GB | str |
nchar(n) |
是的 | 4,000 | str |
nvarchar(n) |
是的 | 4,000 | str |
nvarchar(max) |
是的 | 2 GB | str |
text |
No | 2 GB(已棄用) | str |
ntext |
是的 | 2 GB(已棄用) | str |
基本字串作業
驅動程式會將所有 Microsoft SQL 字串類型映射到 Python str 物件。
插入與取回字串
使用參數化查詢安全地插入並擷取資料庫中的字串資料。
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
# Create temp table for demo
cursor.execute("""
CREATE TABLE #StringDemo (
ID INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(100),
Email NVARCHAR(200)
)
""")
# Insert string data
cursor.execute(
"INSERT INTO #StringDemo (Name, Email) VALUES (%(name)s, %(email)s)",
{"name": "Alice Smith", "email": "alice@example.com"}
)
conn.commit()
# Retrieve string data
cursor.execute("SELECT Name, Email FROM #StringDemo WHERE ID = 1")
row = cursor.fetchone()
print(row.Name) # 'Alice Smith'
print(row.Email) # 'alice@example.com'
具有特殊字元的字串
使用參數化查詢處理引號、角括號及其他字串中的特殊字元。
# Quotes and special characters handled automatically
cursor.execute("""
CREATE TABLE #Notes (
ID INT IDENTITY(1,1) PRIMARY KEY,
Title NVARCHAR(200),
Content NVARCHAR(MAX)
)
""")
cursor.execute(
"INSERT INTO #Notes (Title, Content) VALUES (%(title)s, %(content)s)",
{
"title": "O'Brien's Report",
"content": 'Contains "quotes" and special chars: <>&'
}
)
conn.commit()
Unicode 支援
使用 nvarchar 欄位和 Python str 來儲存和檢索任何語言的文字。
儲存 Unicode 文字
透過將 Python 字串傳遞給參數化查詢,插入 Unicode 內容;驅動程式會將 nvarchar 欄位編碼為 UTF-16LE。
# International characters - use nvarchar columns
cursor.execute("""
CREATE TABLE #Messages (
ID INT IDENTITY(1,1) PRIMARY KEY,
Content NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #Messages (Content) VALUES (%(msg)s)
""", {"msg": "Hello 你好 مرحبا שלום 🎉"})
cursor.execute("SELECT Content FROM #Messages WHERE ID = 1")
row = cursor.fetchone()
print(row.Content) # 'Hello 你好 مرحبا שלום 🎉'
不同文字中的 Unicode
透過使用 nvarchar 欄位與批量插入,支援單一表格中多種語言與腳本。
messages = [
{"lang": "English", "text": "Hello, World!"},
{"lang": "Chinese", "text": "你好,世界!"},
{"lang": "Japanese", "text": "こんにちは世界!"},
{"lang": "Korean", "text": "안녕하세요, 세상!"},
{"lang": "Arabic", "text": "مرحبا بالعالم!"},
{"lang": "Hebrew", "text": "שלום עולם!"},
{"lang": "Russian", "text": "Привет мир!"},
{"lang": "Greek", "text": "Γειά σου Κόσμε!"},
{"lang": "Emoji", "text": "👋🌍✨🎉"},
]
cursor.execute("""
CREATE TABLE #Greetings (
ID INT IDENTITY(1,1) PRIMARY KEY,
Language NVARCHAR(50),
Message NVARCHAR(200)
)
""")
cursor.executemany("""
INSERT INTO #Greetings (Language, Message) VALUES (%(lang)s, %(text)s)
""", messages)
conn.commit()
確保 Unicode 使用 nvarchar 欄位
當你的資料可能包含非 ASCII 字元時,欄位應始終定義為 nvarchar 而非 varchar。
-- For Unicode data, always use nvarchar, not varchar
CREATE TABLE #UnicodeDemo (
ID INT IDENTITY PRIMARY KEY,
Name NVARCHAR(100), -- Supports Unicode
Description NVARCHAR(MAX) -- Supports large Unicode text
);
弦長考量
根據資料長度的一致性,選擇固定長度或可變長度類型。
固定長度與可變長度
Microsoft SQL 的char(n)會在值後方補上尾隨空格,直到達到宣告的長度。 這種填充會浪費可變長度資料的儲存空間,但能提升固定寬度欄位(如國家代碼)的效能。 大多數字串欄位請使用 varchar(n)。
以下範例展示了填充欄與非填充欄在資料檢索上的差異:
# char(6) pads to fixed length
cursor.execute(
"SELECT StateProvinceCode FROM Person.StateProvince WHERE StateProvinceID = 1"
) # nchar(6) column
row = cursor.fetchone()
print(repr(row.StateProvinceCode)) # 'AB ' - right-padded with spaces
# nvarchar stores actual length
cursor.execute(
"SELECT Name FROM Person.StateProvince WHERE StateProvinceID = 1"
) # nvarchar column
row = cursor.fetchone()
print(repr(row.Name)) # 'Alberta' - no padding
處理後方空間
從固定長度字元欄位擷取資料時,請使用 rstrip() 以移除 Microsoft SQL Server 新增的填充空間。
# Strip trailing spaces from char columns
cursor.execute("SELECT ProductNumber FROM Production.Product")
for row in cursor:
code = row.ProductNumber.rstrip() # Remove trailing spaces
print(f"Code: '{code}'")
大型字串(MAX 類型)
nvarchar(max) 和 varchar(max) 類型支援最大可達 2 GB 的字串,非常適合儲存大型文字文件、JSON 或 XML 內容。
# Large text content
large_content = "x" * 100000 # 100K characters
cursor.execute("""
CREATE TABLE #Documents (
ID INT IDENTITY(1,1) PRIMARY KEY,
Content NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #Documents (Content) VALUES (%(content)s)
""", {"content": large_content})
cursor.execute("SELECT Content FROM #Documents WHERE ID = 1")
row = cursor.fetchone()
print(len(row.Content)) # 100000
字串比較與排序
Microsoft SQL 字串比較行為依賴於資料庫或欄位上的整合集。
區分大小寫
Microsoft SQL 字串比較依賴於整合。 預設情況下,大多數資料庫使用大小寫不區分的排序,但你可以用這個 COLLATE 子句覆蓋它。
# Case-insensitive collation (default for many databases)
cursor.execute("SELECT * FROM Person.Person WHERE LastName = %(name)s", {"name": "smith"})
# Might match 'Smith', 'SMITH', 'smith' depending on collation
# For case-sensitive comparison
cursor.execute("""
SELECT * FROM Person.Person
WHERE LastName COLLATE Latin1_General_CS_AS = %(name)s
""", {"name": "Smith"})
LIKE 模式比對
使用含有萬用字元的 LIKE 運算子來搜尋字串模式;使用方括號表示法逸出特殊字元,以比對常值。
# Wildcard searches
search_term = "Road"
cursor.execute("""
SELECT Name FROM Production.Product WHERE Name LIKE %(pattern)s
""", {"pattern": f"%{search_term}%"})
# Escape special characters in search
def escape_like(value: str) -> str:
"""Escape LIKE wildcards in search value."""
return value.replace("[", "[[]").replace("%", "[%]").replace("_", "[_]")
search = "100%"
cursor.execute("""
SELECT Name FROM Production.Product WHERE Name LIKE %(pattern)s
""", {"pattern": f"%{escape_like(search)}%"})
編碼考量
編碼行為取決於 Microsoft SQL 欄位類型及來源排序。
編碼假設與 Unicode 預設值
驅動mssql-python程式會根據 Microsoft SQL 欄位類型自動處理編碼。 預設情況下,字串參數對於 nvarchar 欄位會以 UTF-16LE 傳送,對於 varchar 欄位則會依據資料庫定序傳送:
| 欄位類型 | 線路編碼 | Python 結果 |
|---|---|---|
nvarchar、nchar、ntext |
UTF-16LE |
str (由驅動程式解碼) |
varchar、char、text |
資料庫或欄位排序編碼 |
str (由驅動程式使用原始碼編碼解碼) |
Python 字串內部始終是 Unicode。 當你傳遞參數 str 時,驅動程式會將該參數編碼為目標欄位類型。 預設情況下,驅動程式會以 (Unicode) 格式傳送字串參數 nvarchar ,確保字元無論資料庫整合如何都能被保留。 對於 varchar 欄位,UTF-8 僅適用於資料庫或欄位使用 UTF-8 啟用的排序時。
如果你的欄位是, varchar 且你需要傳送非 Unicode 資料以完全符合欄位類型(例如避免隱含的轉換警告),請使用 setinputsizes() 覆蓋預設:
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
# Create temp table for demo
cursor.execute("CREATE TABLE #AsciiTable (Code VARCHAR(100))")
cursor.setinputsizes([(mssql_python.SQL_VARCHAR, 100, 0)])
cursor.execute(
"INSERT INTO #AsciiTable (Code) VALUES (?)",
("ABC123",)
)
conn.commit()
對大多數應用程式來說,預設行為是正確的。 只有在查詢計畫中看到隱含的轉換警告或需要匹配特定 varchar 彙整時才會覆蓋。
連線編碼
mssql-python 驅動程式會根據 Microsoft SQL Server 版本與設定自動處理連線編碼。 由於 Python 字串是 Unicode,驅動程式會根據目標資料型態適當編碼(UTF-8 或 UTF-16)。 你不需要手動設定連線編碼。
使用舊版定序的 VARCHAR 欄位
使用 Windows-1252(CP1252)定序的資料庫(例如 Latin1_General_CI_AS)會將延伸拉丁字元(例如 €、™ 和帶重音符號的字元)儲存在使用 CP1252 編碼的 varchar 欄位中。 駕駛員在所有平台上都能正確解碼這些字元。
此差異對跨平台部署尤為重要:Windows 上正確讀取的相同varchar資料在 Linux 上也能正確讀取,且無需特殊設定。
# Create a temp table with a varchar column and insert extended Latin characters
cursor.execute("CREATE TABLE #Products (Name VARCHAR(100))")
cursor.execute("INSERT INTO #Products (Name) VALUES (%(name)s)", {"name": "Café €100 ™"})
conn.commit()
# CP1252 characters in varchar columns are decoded correctly on all platforms
cursor.execute("SELECT Name FROM #Products WHERE Name LIKE '%€%'")
for row in cursor:
print(row.Name) # Correct on both Windows and Linux
如果您的結構描述允許,將 varchar 欄位遷移至 nvarchar 可完全避免編碼歧義問題,並支援所有 Unicode 字元。
檔案編碼
讀取檔案插入資料庫時,請指定適當的編碼以保留 Unicode 內容。
# Reading files with explicit encoding
def insert_file_content(cursor, conn, file_path: str, encoding: str = "utf-8"):
with open(file_path, "r", encoding=encoding) as f:
content = f.read()
cursor.execute(
"INSERT INTO #FileContent (Content) VALUES (%(content)s)",
{"content": content}
)
conn.commit()
常見的字串操作
這些範例涵蓋了 Python 和 SQL 中常見的字串操作模式。
串接
你可以先在 Python 裡串接字串,或是用伺服器上的 SQL 字串運算子。
# Concatenate in Python before insert
first_name = "Alice"
last_name = "Smith"
full_name = f"{first_name} {last_name}"
cursor.execute("""
CREATE TABLE #ConcatDemo (
ID INT IDENTITY(1,1) PRIMARY KEY,
FullName NVARCHAR(200)
)
""")
cursor.execute(
"INSERT INTO #ConcatDemo (FullName) VALUES (%(name)s)",
{"name": full_name}
)
# Or concatenate in SQL
cursor.execute("""
SELECT FirstName + ' ' + LastName AS FullName FROM Person.Person
""")
字串格式
在 Python 中套用格式來顯示帶有貨幣、填充或對齊的字串,然後再給使用者看。
from decimal import Decimal
# Format for display
cursor.execute("SELECT Name, ListPrice FROM Production.Product WHERE ListPrice > 0")
for row in cursor.fetchall()[:5]:
print(f"{row.Name}: ${row.ListPrice:.2f}")
# Pad strings
cursor.execute("SELECT ProductNumber FROM Production.Product")
for row in cursor.fetchall()[:5]:
padded = row.ProductNumber.ljust(15) # Left-justify, pad to 15 chars
print(f"[{padded}]")
NULL 與空字串
Microsoft SQL 將 NULL 與空字串('')視為不同的值。 NULL 表示「未知」,而 empty 字串表示「已知為空」。為你的申請選擇一個慣例並保持一致。 大多數應用程式使用 NULL 來表示缺少的可選欄位。
以下範例示範如何區分 NULL 字串與空字串:
# NULL is different from empty string
cursor.execute("""
CREATE TABLE #NullDemo (
ID INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(100),
MiddleName NVARCHAR(100)
)
""")
cursor.execute("""
INSERT INTO #NullDemo (Name, MiddleName)
VALUES (%(name)s, %(middle)s)
""", {"name": "Alice", "middle": None}) # NULL
cursor.execute("""
INSERT INTO #NullDemo (Name, MiddleName)
VALUES (%(name)s, %(middle)s)
""", {"name": "Bob", "middle": ""}) # Empty string
# Query differences
cursor.execute("SELECT * FROM #NullDemo WHERE MiddleName IS NULL")
cursor.execute("SELECT * FROM #NullDemo WHERE MiddleName = ''")
修剪作業
使用 Python 的字串方法,從資料庫中移除前置、後置或兩者的空白。
cursor.execute("SELECT Name FROM Production.Product")
for row in cursor:
# Remove whitespace
trimmed = row.Name.strip() # Both ends
left_trimmed = row.Name.lstrip()
right_trimmed = row.Name.rstrip()
JSON 字串資料
將 JSON 文件存放在 nvarchar(max) 欄位,並用 Microsoft SQL 的 JSON 函式查詢。
Store JSON as nvarchar
將 Python 字典序列化成 JSON 字串並插入 nvarchar 欄位;再取回並反序列化回 Python 物件。
import json
data = {"name": "Alice", "scores": [95, 87, 91], "active": True}
json_string = json.dumps(data)
cursor.execute("""
CREATE TABLE #Configs (
ID INT IDENTITY(1,1) PRIMARY KEY,
ConfigData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #Configs (ConfigData) VALUES (%(data)s)
""", {"data": json_string})
# Retrieve and parse
cursor.execute("SELECT ConfigData FROM #Configs WHERE ID = 1")
row = cursor.fetchone()
config = json.loads(row.ConfigData)
print(config["name"]) # 'Alice'
使用 Microsoft SQL JSON 函式
使用 Microsoft SQL 的 JSON 函式,直接在查詢中解析和過濾 JSON 資料,而非用戶端程式碼。
import json
data = {"name": "Alice", "scores": [95, 87, 91], "active": True}
cursor.execute("""
CREATE TABLE #Configs (
ID INT IDENTITY(1,1) PRIMARY KEY,
ConfigData NVARCHAR(MAX)
)
""")
cursor.execute(
"INSERT INTO #Configs (ConfigData) VALUES (%(data)s)",
{"data": json.dumps(data)}
)
conn.commit()
cursor.execute("""
SELECT JSON_VALUE(ConfigData, '$.name') AS Name
FROM #Configs
WHERE JSON_VALUE(ConfigData, '$.active') = 'true'
""")
for row in cursor:
print(row.Name) # 'Alice'
全文搜尋
可用於 LIKE 模式匹配,或啟用全文索引以進行更進階的文字搜尋。
全文查詢
當無法提供全文索引時,帶有通配字模式的 LIKE 運算子提供了一種直接的全文搜尋替代方案。
# Using CONTAINS (requires full-text index on the table)
cursor.execute("""
SELECT JobTitle FROM HumanResources.Employee
WHERE JobTitle LIKE %(search)s
""", {"search": "%Engineer%"})
# Pattern-based search as an alternative to full-text
cursor.execute("""
SELECT Name FROM Production.Product
WHERE Name LIKE %(search)s
""", {"search": "%Mountain%"})
最佳做法
應用這些指引,正確處理跨語言與編碼的字串資料。
使用 nvarchar 來取得國際資料
如果你不確定某欄是否包含 Unicode,請使用 nvarchar。 儲存成本適中,且可防止字元轉換時的資料遺失。
以下範例展示了定義 Unicode 與僅 ASCII 資料欄位的差異:
-- Good: supports any language
CREATE TABLE #UserProfile (
Name NVARCHAR(100),
Bio NVARCHAR(MAX)
);
-- Limited: ASCII/Latin only
CREATE TABLE #UserProfileAscii (
Name VARCHAR(100),
Bio VARCHAR(MAX)
);
驗證字串長度
插入前請檢查 Python 字串長度,以避免截斷錯誤並提供有意義的錯誤訊息給使用者。
def safe_insert(cursor, name: str, max_length: int = 100):
"""Insert with length validation."""
if len(name) > max_length:
raise ValueError(f"Name exceeds {max_length} characters")
cursor.execute(
"INSERT INTO #UserProfile (Name) VALUES (%(name)s)",
{"name": name}
)
分別處理二進位字串
區分文字字串(Pythonstr、SQL nvarchar)與二進位資料(Pythonbytes、SQL varbinary),以避免編碼問題。
binary_data = b'\x00\x01\x02' # bytes - use varbinary
text_data = "Hello" # str - use nvarchar