Obsługa ciągów znaków i standardu Unicode

Microsoft SQL oferuje wiele typów ciągów znaków, które sterownik mssql-python mapuje na obiekty Pythonstr. Kluczową decyzją jest, czy użyć varchar (nie-Unicode) czy nvarchar (Unicode):

  • Używaj nvarchar , gdy Twoje dane mogą zawierać znaki spoza ASCII, takie jak imiona, adresy lub treści generowane przez użytkowników w dowolnym języku.
  • Używaj varchar , gdy dane są ściśle ASCII (kody, identyfikatory, adresy e-mail) i chcesz oszczędzać miejsce. varchar używa 1 bajtu na znak; nvarchar używa 2 bajtów na znak.
Typ SQL Unicode Maksymalna długość Typ języka Python
char(n) No 8,000 str
varchar(n) No 8,000 str
varchar(max) No 2 GB str
nchar(n) Yes 4,000 str
nvarchar(n) Yes 4,000 str
nvarchar(max) Yes 2 GB str
text No 2 GB (wycofane) str
ntext Yes 2 GB (wycofane) str

Podstawowe operacje na ciągach

Sterownik mapuje wszystkie typy ciągów znaków Microsoft SQL na obiekty Python str.

Wstawiaj i wyciągaj struny

Używaj parametryzowanych zapytań do bezpiecznego wstawiania i pobierania danych łańcuchowych z bazy danych.

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'

Ciągi ze znakami specjalnymi

Obsługa cudzysłowów, nawiasów kątowych i innych znaków specjalnych w łańcuchach za pomocą parametryzowanych zapytań.

# 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()

Obsługa formatu Unicode

Używaj kolumn nvarchar i Python str do przechowywania i pobierania tekstu w dowolnym języku.

Zapisz tekst Unicode

Wstaw zawartość Unicode, przekazując ciągi Python do parametryzowanych zapytań; sterownik koduje je jako UTF-16LE dla kolumn nvarchar.

# 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 w różnych pismach

Wspieraj wiele języków i skryptów w jednej tabeli, używając kolumn nvarchar i wstawiań zbiorczych.

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()

Upewnij się, że kolumny mają typ nvarchar na potrzeby Unicode

Zawsze definiuj kolumny jako nvarchar zamiast varchar, gdy twoje dane mogą zawierać znaki nie-ASCII.

-- 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
);

Rozważania dotyczące długości strun

Wybierz między typami o stałej długości a o zmiennej długości w zależności od tego, jak spójne są długości Twoich danych.

Stała a zmienna długość

char(n) w Microsoft SQL dopełnia wartości spacjami na końcu do zadeklarowanej długości. To wypełnienie marnuje miejsce na dane o zmiennej długości, ale może poprawić wydajność kolumn o stałej szerokości, takich jak kody krajów. Używaj varchar(n) w większości kolumn stringów.

Poniższy przykład pokazuje różnicę w sposobie, w jaki kolumny z wypełnianiem a bez wypełniania obsługują pobieranie danych:

# 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

Obsługa końcowych spacji

Podczas pobierania danych z kolumn char o stałej długości użyj rstrip(), aby usunąć spacje dopełniające dodawane przez 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}'")

Duże ciągi znaków (typy MAX)

Typy nvarchar(max) i varchar(max) obsługują ciągi znaków o rozmiarze do 2 GB, dzięki czemu idealnie nadają się do przechowywania dużych dokumentów tekstowych oraz zawartości JSON lub 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

Porównanie i sortowanie strun

Sposób porównywania ciągów znaków w Microsoft SQL zależy od sortowania ustawionego dla bazy danych lub kolumny.

Uwzględnij wielkość liter

Porównanie ciągów znaków w Microsoft SQL zależy od sortowania. Domyślnie większość baz danych używa sortowania bez rozróżniania wielkości liter, ale można to nadpisać klauzulą 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"})

Dopasowywanie wzorców LIKE

Używaj operatora LIKE ze znakami wieloznacznymi do wyszukiwania wzorców ciągów znaków; stosuj notację nawiasową dla znaków specjalnych, aby dopasować je dosłownie.

# 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)}%"})

Rozważania dotyczące kodowania

Sposób kodowania zależy od typu kolumny Microsoft SQL oraz źródłowych ustawień sortowania.

Założenia kodowania i domyślne ustawienia Unicode

Sterownik mssql-python automatycznie obsługuje kodowanie na podstawie typu kolumny Microsoft SQL. Domyślnie parametry tekstowe są wysyłane jako UTF-16LE dla kolumn nvarchar, a dla kolumn varchar zgodnie z ustawieniami sortowania bazy danych:

Typ kolumny Kodowanie przewodowe Wynik Python
nvarchar, nchar, ntext UTF-16LE str (odszyfrowane przez kierowcę)
varchar, char, text Kodowanie zbiorcze bazy danych lub kolumn str (dekodowane przez sterownik przy użyciu kodowania źródłowego)

Łańcuchy znaków w Pythonie są zawsze wewnętrznie reprezentowane w standardzie Unicode. Gdy przekazujesz parametr str, sterownik koduje go dla docelowego typu kolumny. Domyślnie sterownik wysyła parametry ciągu jako nvarchar (Unicode), co zapewnia, że znaki są zachowane niezależnie od zestawienia w bazie danych. W przypadku kolumn varchar kodowanie UTF-8 ma zastosowanie tylko wtedy, gdy baza danych lub kolumna używa sortowania obsługującego UTF-8.

Jeśli kolumna ma typ varchar i musisz wysłać dane inne niż Unicode, aby dokładnie odpowiadały typowi kolumny (na przykład aby uniknąć ostrzeżeń o niejawnej konwersji), użyj setinputsizes(), aby zastąpić ustawienie domyślne:

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()

W większości zastosowań domyślne zachowanie jest poprawne. Nadpisujesz tylko wtedy, gdy widzisz ukryte ostrzeżenia o konwersji w planach zapytań lub musisz dopasować konkretną varchar sortację.

Kodowanie połączeń

Sterownik mssql-python automatycznie obsługuje kodowanie połączenia na podstawie wersji i konfiguracji Microsoft SQL Server. Ponieważ ciągi Python są Unicode, sterownik koduje je odpowiednio (UTF-8 lub UTF-16) dla docelowego typu danych. Nie musisz ręcznie konfigurować kodowania połączeń.

Kolumny VARCHAR ze starszymi regułami sortowania

Bazy danych z kolacjami Windows-1252 (CP1252), takie jak Latin1_General_CI_AS, przechowują rozszerzone znaki łacińskie (na przykład , , oraz znaki akcentowane) w varchar kolumnach z wykorzystaniem kodowania CP1252. Sterownik poprawnie dekoduje te znaki na wszystkich platformach.

Ta różnica ma znaczenie przy wdrożeniach międzyplatformowych: te same varchar dane, które odczytują poprawnie na Windows, odczytują poprawnie także na Linuksie, bez potrzeby specjalnej konfiguracji.

# 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

Jeśli Twój schemat na to pozwala, migracja kolumn z varchar do nvarchar całkowicie eliminuje niejednoznaczność kodowania i obsługuje wszystkie znaki Unicode.

Kodowanie plików

Podczas odczytu plików do wstawienia do bazy danych określ odpowiednie kodowanie, aby zachować zawartość 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()

Typowe operacje na ciągach

Te przykłady obejmują typowe wzorce manipulacji ciągami znaków zarówno w Python, jak i SQL.

Łączenie

Możesz łączyć łańcuchy w Python przed wstawieniem lub używać operatorów łańcuchów SQL na serwerze.

# 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
""")

Formatowanie ciągu

Zastosuj formatowanie w Python, aby wyświetlać ciągi znaków z walutą, wypełnieniem lub wyrównaniem przed ich pokazaniem użytkownikom.

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 kontra pusty ciąg

Microsoft SQL traktuje NULL i pusty ciąg ('') jako różne wartości. NULL oznacza "nieznany", natomiast pusty ciąg oznacza "wiadomo, że jest pusty". Wybierz jedną konwencję do swojej aplikacji i bądź konsekwentny. Większość aplikacji używa NULL dla brakujących pól opcjonalnych.

Poniższy przykład pokazuje, jak rozróżnić ciąg NULL od ciągu pustego:

# 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 = ''")

Operacje trimowania

Użyj metod ciągów znaków w Pythonie, aby usunąć białe znaki z początku, z końca lub z obu stron wartości pobranych z bazy danych.

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()

Dane łańcuchowe JSON

Przechowuj dokumenty JSON w kolumnach nvarchar(max) i zapytuj je za pomocą funkcji JSON Microsoft SQL.

Zapisz JSON jako nvarchar

Serializuj słowniki Python do ciągów JSON i wstaw je do kolumn nvarchar; pobierz i deserializuj je z powrotem do obiektów 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'

Użyj funkcji Microsoft SQL JSON

Używaj funkcji JSON Microsoft SQL do analizowania i filtrowania danych JSON bezpośrednio w zapytaniach zamiast w kodzie klienta.

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'

Zastosowanie LIKE do dopasowania wzorców lub włączenie indeksu pełnego tekstu dla bardziej zaawansowanego wyszukiwania tekstowego.

Zapytania pełnotekstowe

Operator LIKE używany z wzorcami wieloznacznymi zapewnia prostą alternatywę dla wyszukiwania pełnotekstowego, gdy indeks pełnotekstowy nie jest dostępny.

# 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%"})

Najlepsze rozwiązania

Stosuj te wytyczne, aby poprawnie obsługiwać dane ciągów znaków w różnych językach i kodowaniach.

Użyj nvarchar do danych międzynarodowych

Jeśli nie jesteś pewien, czy kolumna może zawierać Unicode, użyj nvarchar. Koszt przechowywania jest umiarkowany i zapobiega utracie danych podczas konwersji znaków.

Poniższy przykład pokazuje różnicę między definiowaniem kolumn dla danych Unicode a danych tylko 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)
);

Walidacja długości ciągu

Sprawdź długość ciągu w Pythonie przed wstawieniem, aby zapobiec błędom związanym z obcięciem danych i zapewnić użytkownikom czytelne komunikaty o błędach.

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}
    )

Traktuj ciągi binarne osobno

Rozróżnij ciągi tekstowe (Pythonstr, SQLnvarchar) od danych binarnych (Pythonbytes, SQLvarbinary), aby uniknąć problemów z kodowaniem.

binary_data = b'\x00\x01\x02'  # bytes - use varbinary
text_data = "Hello"            # str - use nvarchar