Manipular strings e Unicode

O Microsoft SQL fornece múltiplos tipos de string que o driver mssql-python mapeia para objetos Pythonstr. A decisão chave é se usar varchar (não Unicode) ou nvarchar (Unicode):

  • Use nvarchar quando seus dados podem conter caracteres fora do ASCII, como nomes, endereços ou conteúdo gerado pelo usuário em qualquer idioma.
  • Use varchar quando os dados forem estritamente ASCII (códigos, identificadores, endereços de e-mail) e você quiser economizar armazenamento. varchar usa 1 byte por caractere; nvarchar Usa 2 bytes por caractere.
Tipo de SQL Unicode Comprimento Máximo Tipo de Python
char(n) No 8,000 str
varchar(n) No 8,000 str
varchar(max) No 2 GB str
nchar(n) Sim 4,000 str
nvarchar(n) Sim 4,000 str
nvarchar(max) Sim 2 GB str
text No 2 GB (descontinuado) str
ntext Sim 2 GB (descontinuado) str

Operações básicas de cadeias de caracteres

O driver mapeia todos os tipos de string SQL do Microsoft para objetos Pythonstr.

Inserir e recuperar cadeias de caracteres

Use consultas parametrizadas para inserir e buscar dados de string do banco de dados com segurança.

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'

Cadeias de caracteres com caracteres especiais

Trate aspas, colchetes angulares e outros caracteres especiais em strings usando consultas parametrizadas.

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

Suporte de Unicode

Use colunas nvarchar e Python str para armazenar e recuperar texto em qualquer linguagem.

Armazenar texto Unicode

Insira conteúdo Unicode passando strings em Python para consultas parametrizadas; o driver as codifica como UTF-16LE para colunas 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 em diferentes sistemas de escrita

Suporta múltiplas linguagens e scripts em uma única tabela usando colunas nvarchar e inserções em massa.

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

Certifique-se de usar colunas do tipo nvarchar para dar suporte a Unicode

Sempre defina as colunas como nvarchar em vez de varchar quando seus dados possam conter caracteres que não sejam 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
);

Considerações sobre o comprimento das cordas

Escolha entre tipos de comprimento fixo e variável com base na consistência dos seus dados.

Comprimento fixo versus comprimento variável

O char(n) do Microsoft SQL preenche os valores com espaços à direita até o comprimento declarado. Esse preenchimento desperdiça armazenamento para dados de comprimento variável, mas pode melhorar o desempenho para colunas de largura fixa, como códigos de país. Use varchar(n) para a maioria das colunas de string.

O exemplo a seguir mostra a diferença entre como colunas preenchidas e não preenchidas lidam com a recuperação de dados:

# 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

Tratar espaços à direita

Ao recuperar dados de colunas de caracteres de comprimento fixo, use rstrip() para remover os espaços de preenchimento adicionados pelo 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}'")

Cadeias de caracteres grandes (tipos MAX)

Os tipos nvarchar(max) e varchar(max) suportam cadeias de caracteres de até 2 GB, ideais para armazenar documentos de texto extensos, conteúdo JSON ou 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

Comparação e colação de strings

O comportamento da comparação de strings no Microsoft SQL depende da ordenação definida no banco de dados ou na coluna.

Diferenciar maiúsculas de minúsculas

A comparação de cadeias de caracteres no Microsoft SQL depende do agrupamento. Por padrão, a maioria dos bancos de dados usa ordenação que não diferencia maiúsculas de minúsculas, mas você pode substituir esse comportamento com a cláusula 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"})

Correspondência de padrões LIKE

Use o operador LIKE com caracteres curinga para procurar padrões de cadeia de caracteres; escape os caracteres especiais com notação de colchetes para corresponder aos caracteres literais.

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

Considerações de codificação

O comportamento de codificação depende do tipo de coluna do Microsoft SQL e da coletão de origem.

Suposições de codificação e padrões Unicode

O driver mssql-python processa a codificação automaticamente com base no tipo de coluna do Microsoft SQL. Por padrão, os parâmetros de cadeia de caracteres são enviados como UTF-16LE para colunas nvarchar e de acordo com a ordenação do banco de dados para colunas varchar:

Tipo de coluna Codificação por fio Resultado em Python
nvarchar, nchar, ntext UTF-16LE str (decifrado pelo motorista)
varchar, char, text Codificação por colação de banco de dados ou colunas str (decodificado pelo driver usando a codificação de origem)

As strings de Python são sempre Unicode internamente. Quando você passa um str parâmetro, o driver o codifica para o tipo de coluna alvo. Por padrão, o driver envia parâmetros de string como nvarchar (Unicode), o que garante que os caracteres sejam preservados independentemente da colação do banco de dados. Para colunas varchar, o UTF-8 se aplica somente quando o banco de dados ou a coluna usa uma ordenação com suporte a UTF-8.

Se sua coluna for varchar e você precisar enviar dados não-Unicode para corresponder exatamente ao tipo de coluna (por exemplo, para evitar avisos implícitos de conversão), use setinputsizes() para sobrescrever o padrão:

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

Para a maioria das aplicações, o comportamento padrão está correto. Sobrescreva apenas quando você vir avisos de conversão implícita em planos de consulta ou precisar usar uma ordenação específica varchar.

Codificação de conexão

O driver mssql-python gerencia automaticamente a codificação da conexão com base na versão e configuração do Microsoft SQL Server. Como as strings de Python são Unicode, o driver as codifica adequadamente (UTF-8 ou UTF-16) para o tipo de dado alvo. Você não precisa configurar a codificação de conexão manualmente.

Colunas VARCHAR com colações legadas

Bancos de dados com colações Windows-1252 (CP1252), como Latin1_General_CI_AS, armazenam caracteres latinos estendidos (por exemplo, , , e caracteres acentuados) em varchar colunas usando a codificação CP1252. O driver decodifica esses caracteres corretamente em todas as plataformas.

Essa diferença é importante para implantações multiplataforma: os mesmos varchar dados que lêem corretamente no Windows também são lidos corretamente no Linux, sem necessidade de configuração especial.

# 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 seu esquema permitir, migrar varchar colunas para nvarchar evita completamente ambiguidade de codificação e suporta todos os caracteres Unicode.

Codificação de arquivo

Ao ler arquivos para inserir no banco de dados, especifique a codificação apropriada para preservar o conteúdo 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()

Operações de cadeia de caracteres comuns

Esses exemplos cobrem padrões comuns de manipulação de strings tanto em Python quanto em SQL.

Concatenação

Você pode concatenar cadeias de caracteres seja em Python, antes da inserção, seja usando os operadores de cadeia de caracteres do SQL no servidor.

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

Formatação da cadeia de caracteres

Aplique formatação em Python para exibir strings com moeda, enchimento ou alinhamento antes de mostrá-las aos usuários.

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 cadeia vazia

O Microsoft SQL trata NULL e string vazio ('') como valores diferentes. NULL significa "desconhecido", enquanto string vazia significa "sabidamente vazia". Escolha uma convenção para sua aplicação e seja consistente. A maioria das aplicações usa NULL para campos opcionais ausentes.

O exemplo a seguir demonstra como distinguir entre NULL e cadeia vazia:

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

Operações de recorte

Use os métodos de string do Python para remover espaços em branco iniciais, finais ou ambos dos valores recuperados do banco de dados.

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

Dados de string JSON

Armazene documentos JSON em colunas nvarchar(max) e consulte-os com as funções JSON do Microsoft SQL.

Armazene JSON como nvarchar

Serialize dicionários Python em strings JSON e insira-os em colunas nvarchar; recupere e desserialize novamente para objetos 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'

Use funções JSON SQL do Microsoft

Use as funções JSON do Microsoft SQL para analisar e filtrar dados JSON diretamente nas consultas, em vez de no código do cliente.

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'

Use LIKE para correspondência de padrões, ou ative um índice de texto completo para busca de texto mais avançada.

Consultas de texto completo

O LIKE operador com padrões de coringa oferece uma alternativa direta à busca em texto completo quando um índice completo não está disponível.

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

Práticas recomendadas

Aplique essas diretrizes para lidar corretamente com dados de string entre linguagens e codificações.

Use nvarchar para dados internacionais

Se você não tem certeza se uma coluna pode conter Unicode, use nvarchar. O custo de armazenamento é modesto e evita a perda de dados causada pela conversão de caracteres.

O exemplo a seguir mostra a diferença entre definir colunas para dados Unicode e apenas 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)
);

Validar o comprimento da string

Verifique o comprimento da string em Python antes de inserir para evitar erros de truncamento e fornecer mensagens de erro significativas aos usuários.

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

Lidar com as cadeias binárias separadamente

Distinga entre strings de texto (Pythonstr, SQLnvarchar) e dados binários (Pythonbytes, SQLvarbinary) para evitar problemas de codificação.

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