Gestire stringhe e Unicode

Microsoft SQL fornisce più tipi di stringhe che il driver mssql-python mappa agli oggetti Pythonstr. La decisione chiave è se usare varchar (non-Unicode) o nvarchar (Unicode):

  • Usali nvarchar quando i tuoi dati potrebbero contenere caratteri esterni ad ASCII, come nomi, indirizzi o contenuti generati dagli utenti in qualsiasi lingua.
  • Usa varchar quando i dati sono strettamente ASCII (codici, identificatori, indirizzi email) e vuoi risparmiare spazio. varchar usa 1 byte per carattere; nvarchar Usa 2 byte per carattere.
Tipo SQL Unicode Lunghezza massima Tipo Python
char(n) No 8,000 str
varchar(n) No 8,000 str
varchar(max) No 2GB str
nchar(n) 4,000 str
nvarchar(n) 4,000 str
nvarchar(max) 2GB str
text No 2 GB (obsoleto) str
ntext 2 GB (obsoleto) str

Operazioni di base su stringhe

Il driver mappa tutti i tipi di stringhe Microsoft SQL su oggetti Pythonstr.

Inserire e recuperare le corde

Usa query parametrizzate per inserire e recuperare in sicurezza i dati delle stringhe dal database.

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'

Stringhe con caratteri speciali

Gestisci virgolette, parentesi angolari e altri caratteri speciali nelle stringhe usando query parametrizzate.

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

Supporto Unicode

Usa colonne nvarchar e Python str per memorizzare e recuperare testo in qualsiasi linguaggio.

Memorizza testo Unicode

Inserite contenuto Unicode passando stringhe Python tramite query con parametri; il driver le codifica come UTF-16LE nelle colonne 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 in diversi alfabeti

Supporta più linguaggi e script in una singola tabella utilizzando colonne nvarchar e inserimenti massivi.

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

Assicurarsi che le colonne siano di tipo nvarchar per Unicode

Definisci sempre le colonne come nvarchar invece che varchar quando i tuoi dati potrebbero contenere caratteri non 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
);

Considerazioni sulla lunghezza della corda

Scegli tra tipi a lunghezza fissa e variabile in base a quanto sono coerenti le lunghezze dei tuoi dati.

Lunghezza fissa contro lunghezza variabile

Microsoft SQL char(n) riempie i valori con spazi finali fino alla lunghezza dichiarata. Questo riempimento spreca lo spazio di archiviazione per dati a lunghezza variabile ma può migliorare le prestazioni per colonne a larghezza fissa, come i codici paese. Usare varchar(n) per la maggior parte delle colonne di tipo stringa.

Il seguente esempio mostra la differenza tra come le colonne imbottite e non imbottite gestiscono il recupero dati:

# 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

Gestire gli spazi finali

Quando si recuperano dati da colonne di carattere di lunghezza fissa, si usano rstrip() per rimuovere gli spazi di riempimento aggiunti da 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}'")

Stringhe di grandi dimensioni (tipi MAX)

I nvarchar(max) tipi and varchar(max) supportano stringhe fino a 2 GB, ideali per memorizzare grandi documenti di testo, contenuti JSON o 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

Confronto e collazione delle stringhe

Il comportamento di confronto delle stringhe SQL di Microsoft dipende dal collation set sul database o sulla colonna.

Distinzione tra maiuscole e minuscole

Il confronto delle stringhe SQL di Microsoft dipende dalla collazione. Per impostazione predefinita, la maggior parte dei database usa una regola di confronto senza distinzione tra maiuscole e minuscole, ma puoi ignorare questa impostazione con la clausola 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"})

Corrispondenza con modelli LIKE

Usa l'operatore LIKE con caratteri wildcard per cercare modelli di stringa; esegui l'escape dei caratteri speciali con la notazione tra parentesi quadre per trovare corrispondenze letterali.

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

Considerazioni di codifica

Il comportamento della codifica dipende dal tipo di colonna di Microsoft SQL e dalle regole di confronto della sorgente.

Assunzioni di codifica e predefiniti Unicode

Il mssql-python driver gestisce automaticamente la codifica in base al tipo di colonna Microsoft SQL. Per impostazione predefinita, i parametri di tipo stringa sono inviati in formato UTF-16LE per le colonne nvarchar e secondo le regole di confronto del database per le colonne varchar:

Tipo di colonna Codifica su filo Risultato Python
nvarchar, nchar, ntext UTF-16LE str (decodificato dal conducente)
varchar, char, text Codifica per database o collazione di colonne str (decodificata dal driver usando la codifica sorgente)

Le stringhe Python sono sempre Unicode internamente. Quando superi un str parametro, il driver lo codifica per il tipo di colonna target. Di default, il driver invia i parametri della stringa come nvarchar (Unicode), il che garantisce che i caratteri vengano preservati indipendentemente dalla collazione del database. Per le varchar colonne, UTF-8 si applica solo quando il database o la colonna utilizza una collazione abilitata a UTF-8.

Se la tua colonna è varchar e devi inviare dati non Unicode per corrispondere esattamente al tipo di colonna (ad esempio, per evitare avvisi di conversione impliciti), usa setinputsizes() per sovrascrivere il predefinito:

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

Per la maggior parte delle applicazioni, il comportamento predefinito è corretto. Esegui l'override solo quando vedi avvisi di conversione implicita nei piani di query o devi usare una specifica varchar regola di confronto.

Codifica delle connessioni

Il driver mssql-python gestisce automaticamente la codifica della connessione in base alla versione e alla configurazione di Microsoft SQL Server. Poiché le stringhe Python sono Unicode, il driver le codifica in modo appropriato (UTF-8 o UTF-16) per il tipo di dato target. Non è necessario configurare manualmente la codifica della connessione.

Colonne VARCHAR con regole di confronto precedenti

I database con collazioni Windows-1252 (CP1252), come Latin1_General_CI_AS, memorizzano caratteri latini estesi (ad esempio, , , e caratteri accentuati) in varchar colonne utilizzando la codifica CP1252. Il pilota decodifica correttamente questi caratteri su tutte le piattaforme.

Questa differenza è importante per le implementazioni multipiattaforma: gli stessi varchar dati che si leggono correttamente su Windows si leggono correttamente anche su Linux, senza alcuna configurazione particolare richiesta.

# 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

Se il tuo schema lo permette, migrare varchar le colonne a nvarchar evita completamente ambiguità di codifica e supporta tutti i caratteri Unicode.

Codifica file

Quando si leggono i file da inserire nel database, specificare la codifica appropriata per preservare il contenuto 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()

Operazioni di stringa comuni

Questi esempi coprono i comuni pattern di manipolazione delle stringhe sia in Python che in SQL.

Concatenazione

Puoi concatenare stringhe sia in Python prima di inserirle sia usando gli operatori di stringhe di SQL sul server.

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

Formattazione di stringhe

Applica la formattazione in Python per visualizzare le stringhe con valuta, riempimento o allineamento prima di mostrarle agli utenti.

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 contro stringa vuota

Microsoft SQL tratta NULL e stringa vuota ('') come valori diversi. NULL significa "sconosciuto" mentre stringa vuota significa "nota per essere vuota". Scegli una convention per la tua candidatura e sii coerente. La maggior parte delle applicazioni usa NULL per i campi opzionali mancanti.

Il seguente esempio dimostra come distinguere tra stringa NULL e vuota:

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

Operazioni di trim

Usa i metodi per le stringhe di Python per rimuovere gli spazi bianchi iniziali, finali o entrambi dai valori recuperati dal database.

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

Dati di stringhe JSON

Memorizza i documenti JSON nelle colonne nvarchar(max) e interrogali con le funzioni JSON di Microsoft SQL.

Memorizza JSON come nvarchar

Serializzare i dizionari Python in stringhe JSON e inserirli nelle colonne nvarchar; recuperarli e deserializzarli di nuovo in oggetti 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'

Usa le funzioni JSON SQL di Microsoft

Usa le funzioni JSON di Microsoft SQL per analizzare e filtrare i dati JSON direttamente nelle query invece che nel codice client.

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'

Usalo LIKE per il pattern matching, oppure abilita un indice a testo completo per una ricerca testuale più avanzata.

Query in testo completo

L'operatore LIKE con schemi jolly offre un'alternativa semplice alla ricerca a testo intero quando non è disponibile un indice a testo completo.

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

Procedure consigliate

Applica queste linee guida per gestire correttamente i dati delle stringhe tra linguaggi e codifiche.

Usa nvarchar per i dati internazionali

Se non sei sicuro che una colonna possa contenere Unicode, usa nvarchar. Il costo di archiviazione è modesto e previene la perdita di dati dovuta alla conversione dei caratteri.

Il seguente esempio mostra la differenza tra definire colonne per dati Unicode e solo 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)
);

Validare la lunghezza della stringa

Controlla la lunghezza della stringa in Python prima di inserirla per prevenire errori di troncamento e fornire messaggi di errore significativi agli utenti.

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

Gestisci separatamente le stringhe binarie

Distingui tra stringhe di testo (Pythonstr, SQLnvarchar) e dati binari (Pythonbytes, SQLvarbinary) per evitare problemi di codifica.

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