Práce s řetězci a Unicode

Microsoft SQL poskytuje několik typů řetězců, které mssql-python ovladač mapuje na objekty Pythonstr. Klíčové rozhodnutí je, zda použít varchar (ne-Unicode) nebo nvarchar (Unicode):

  • Používejte nvarchar je tehdy, když vaše data mohou obsahovat znaky mimo ASCII, například jména, adresy nebo uživatelsky generovaný obsah v jakémkoli jazyce.
  • Používejte varchar je, když jsou data striktně ASCII (kódy, identifikátory, e-mailové adresy) a chcete šetřit místo. varchar používá 1 bajt na znak; nvarchar používá 2 bajty na znak.
Typ SQL Unicode Maximální délka Typ Pythonu
char(n) Ne 8 000 str
varchar(n) Ne 8 000 str
varchar(max) Ne 2 GB str
nchar(n) Yes 4,000 str
nvarchar(n) Yes 4,000 str
nvarchar(max) Yes 2 GB str
text Ne 2 GB (zastaralé) str
ntext Yes 2 GB (zastaralé) str

Základní operace s řetězci

Ovladač mapuje všechny typy řetězců Microsoft SQL na objekty jazyka Python str.

Vložte a vytáhněte struny

Používejte parametrizované dotazy k bezpečnému vkládání a načítání řetězcových dat z databáze.

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'

Řetězce se speciálními znaky

Zvládejte uvozovky, úhlové závorky a další speciální znaky v řetězcích pomocí parametrizovaných dotazů.

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

Podpora kódování Unicode

Používejte sloupce nvarchar a Python str k ukládání a načítání textu v jakémkoli jazyce.

Uložit text Unicode

Vložte Unicode obsah předáním Python řetězců parametrizovaným dotazům; ovladač je kóduje jako UTF-16LE pro sloupce 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 v různých písmech

Podporujte více jazyků a skriptů v jedné tabulce pomocí sloupců nvarchar a hromadných vkladů.

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

Zajistěte sloupce nvarchar pro Unicode

Vždy definujte sloupce jako nvarchar místo varchar, pokud vaše data mohou obsahovat znaky mimo 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
);

Aspekty délky řetězce

Vyberte si mezi typy s pevnou délkou a proměnnou délkou podle toho, jak konzistentní jsou délky dat.

Pevná versus proměnná délka

Microsoft SQL doplňuje hodnoty char(n) koncovými mezerami na deklarovanou délku. Toto vyplňování plýtvá úložištěm pro data s proměnnou délkou, ale může zlepšit výkon u sloupců s pevnou šířkou, jako jsou kódy zemí. Použijte varchar(n) pro většinu řetězcových sloupců.

Následující příklad ukazuje rozdíl v tom, jak sloupce s doplňováním mezer a bez doplňování mezer zacházejí s načítáním dat:

# 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

Manipulace s vlečnými prostory

Při načítání dat ze sloupců typu char s pevnou délkou použijte rstrip() k odstranění doplňovacích mezer přidaných serverem 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}'")

Velké struny (typy MAX)

Typy nvarchar(max) and varchar(max) podporují řetězce až do 2 GB, ideální pro ukládání rozsáhlých textových dokumentů, JSON nebo XML obsahu.

# 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

Porovnání a třídění strun

Chování porovnávání řetězců v Microsoft SQL závisí na řazení nastaveném pro databázi nebo sloupec.

Citlivost na velikost písmen

Porovnávání řetězců v Microsoft SQL závisí na řazení. Ve výchozím nastavení většina databází používá sortaci bez rozlišení na velká písmena, ale tuto možnost lze přepsat pomocí klauzule 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"})

Porovnávání podle vzoru LIKE

Použijte operátor LIKE se zástupnými znaky k vyhledávání vzorů v řetězcích; speciální znaky zapište pomocí hranatých závorek, aby odpovídaly doslovně.

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

Úvahy ohledně kódování

Chování kódování závisí na typu sloupce v Microsoft SQL a na kolaci zdroje.

Předpoklady kódování a Unicode výchozí nastavení

Ovladač mssql-python automaticky zpracovává kódování na základě typu sloupce Microsoft SQL. Ve výchozím nastavení se řetězcové parametry odesílají jako UTF-16LE pro sloupce nvarchar a podle řazení databáze pro sloupce varchar:

Typ sloupce Drátové kódování Výsledek Python
nvarchar, nchar, ntext UTF-16LE str (rozluštěno řidičem)
varchar, char, text Kódování třídění databází nebo sloupců str (dekódováno ovladačem pomocí kódování zdroje)

Python řetězce jsou vždy interně Unicode. Když předáte parametr str, ovladač ho zakóduje pro typ cílového sloupce. Ve výchozím nastavení ovladač odesílá parametry řetězce jako ( nvarchar Unicode), což zajišťuje, že znaky jsou zachovány bez ohledu na klasifikaci databáze. Pro varchar sloupce se UTF-8 uplatňuje pouze tehdy, když databáze nebo sloupec používá kolaci s podporou UTF-8.

Pokud má váš sloupec typ varchar a potřebujete odesílat data jiná než Unicode, aby přesně odpovídala typu sloupce (například abyste se vyhnuli upozorněním na implicitní převod), použijte setinputsizes() k přepsání výchozího nastavení:

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

Ve většině aplikací je výchozí chování správné. Přepsat pouze tehdy, když v plánech dotazů vidíte varování na implicitní převod nebo když potřebujete použít konkrétní kolaci varchar.

Kódování spojení

Ovladač mssql-python automaticky zajišťuje kódování připojení na základě verze a konfigurace Microsoft SQL Server. Protože řetězce v Python jsou Unicode, ovladač je správně kóduje (UTF-8 nebo UTF-16) pro cílový datový typ. Není potřeba ručně konfigurovat kódování připojení.

Sloupce VARCHAR se starším řazením

Databáze s řazením Windows-1252 (CP1252), například Latin1_General_CI_AS, ukládají rozšířené latinské znaky (například , a znaky s diakritikou) do sloupců varchar pomocí kódování CP1252. Ovladač tyto znaky správně dekóduje na všech platformách.

Tento rozdíl je důležitý pro multiplatformní nasazení: stejná varchar data, která se čtou správně ve Windows, čtou správně i na Linuxu, bez nutnosti speciální konfigurace.

# 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

Pokud to vaše schéma umožňuje, migrace sloupců varchar na nvarchar se zcela vyhne nejednoznačnosti kódování a podporuje všechny znaky Unicode.

Kódování souborů

Při čtení souborů pro vložení do databáze určete vhodné kódování pro zachování Unicode obsahu.

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

Běžné operace s řetězci

Tyto příklady pokrývají běžné vzory manipulace s řetězci jak v Python, tak v SQL.

Zřetězení

Řetězce můžete spojovat buď v Python před vložením, nebo pomocí SQL řetězcových operátorů na serveru.

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

Formátování řetězců

Pomocí formátování v Pythonu můžete před zobrazením uživatelům upravit řetězce tak, aby zobrazovaly měnu, doplnění mezer nebo zarovnání.

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 versus prázdný řetězec

Microsoft SQL považuje NULL a prázdný řetězec ('') za různé hodnoty. NULL znamená „neznámý“, zatímco prázdný řetězec znamená, že je známo, že je prázdný. Zvolte pro svou aplikaci jednu konvenci a dodržujte ji důsledně. Většina aplikací používá NULL pro chybějící volitelné pole.

Následující příklad ukazuje, jak rozlišit mezi NULL a prázdným řetězcem:

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

Operace oříznutí

Použijte řetězcové metody v Python k odstranění úvodních, závěrečných nebo obou mezerových míst z hodnot získaných z databáze.

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 řetězcová data

Ukládejte JSON dokumenty do sloupců nvarchar(max) a dotazujte je pomocí JSON funkcí Microsoft SQL.

Uložit JSON jako nvarchar

Serializujte Python slovníky do JSON řetězců a vložte je do sloupců nvarchar; načtěte je a deserializujte zpět do Python objektů.

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'

Používejte funkce Microsoft SQL JSON

Použijte JSON funkce Microsoft SQL pro analýzu a filtrování JSON dat přímo v dotazech místo v klientském kódu.

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'

Použijte LIKE pro sladění vzorů, nebo povolte index plného textu pro pokročilejší vyhledávání textu.

Dotazy v plném textu

Operátor LIKE s divokými vzory poskytuje přímočarou alternativu k vyhledávání v plném textu, když není k dispozici index plného textu.

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

Osvědčené postupy

Aplikujte tyto pokyny pro správné zpracování řetězcových dat napříč jazyky a kódováními.

Použijte nvarchar pro mezinárodní data

Pokud si nejste jisti, zda sloupec může obsahovat Unicode, použijte nvarchar. Náklady na úložiště jsou skromné a zabraňují ztrátě dat při konverzi znaků.

Následující příklad ukazuje rozdíl mezi definováním sloupců pro Unicode a data pouze s 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)
);

Validovat délku řetězce

Před vložením zkontrolujte délku řetězce v Python, abyste předešli chybám při zkrácení a poskytli uživatelům smysluplné chybové zprávy.

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

Binární řetězce zpracovávejte odděleně

Rozlišujte mezi textovými řetězci (Pythonstr, SQLnvarchar) a binárními daty (Pythonbytes, SQLvarbinary), abyste se vyhnuli problémům s kódováním.

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