Manipular cadenas y Unicode

Microsoft SQL proporciona varios tipos de cadenas que el controlador mssql-python mapea a objetos Pythonstr. La decisión clave es si usar varchar (no Unicode) o nvarchar (Unicode):

  • Úsalo nvarchar cuando tus datos puedan contener caracteres fuera de ASCII, como nombres, direcciones o contenido generado por usuarios en cualquier idioma.
  • Úsalo varchar cuando los datos son estrictamente ASCII (códigos, identificadores, direcciones de correo electrónico) y quieres ahorrar almacenamiento. varchar usa 1 byte por carácter; nvarchar Usa 2 bytes por carácter.
Tipo de SQL Unicode Longitud máxima Tipo de 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 (en desuso) str
ntext 2 GB (en desuso) str

Operaciones básicas de cadenas

El controlador asigna todos los tipos de cadenas SQL de Microsoft a objetos Pythonstr.

Insertar y recuperar cuerdas

Utiliza consultas parametrizadas para insertar y obtener datos de cadenas de la base de datos de forma segura.

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'

Cadenas con caracteres especiales

Maneja comillas, corchetes angulares y otros caracteres especiales en cadenas 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()

Compatibilidad con Unicode

Usa columnas nvarchar y Python str para almacenar y recuperar texto en cualquier lenguaje.

Almacenar texto Unicode

Inserta contenido Unicode pasando cadenas de Python a consultas parametrizadas; el controlador las codifica como UTF-16LE para columnas 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 en diferentes escrituras

Admita varios idiomas y sistemas de escritura en una sola tabla mediante columnas nvarchar e inserciones masivas.

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

Asegúrese de que las columnas sean de tipo nvarchar para Unicode

Define siempre las columnas como nvarchar en lugar de varchar cuando tus datos puedan contener caracteres no 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
);

Consideraciones sobre la longitud de las cuerdas

Elige entre tipos de longitud fija y variable según la consistencia de tus datos.

Longitud fija frente a variable

Microsoft SQL rellena los valores con espacios finales hasta la longitud declarada. Este relleno desperdicia almacenamiento para datos de longitud variable, pero puede mejorar el rendimiento para columnas de ancho fijo, como códigos de país. Use varchar(n) para la mayoría de las columnas de texto.

El siguiente ejemplo muestra la diferencia en cómo las columnas rellenadas frente a las no acolchadas gestionan la recuperación de datos:

# 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

Gestionar espacios finales

Al recuperar datos de columnas de caracteres de longitud fija, úsalo rstrip() para eliminar los espacios de relleno añadidos por 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}'")

Cadenas grandes (tipos MAX)

Los nvarchar(max) tipos y varchar(max) soportan cadenas de hasta 2 GB, ideales para almacenar documentos de texto grandes, contenido 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

Comparación y colación de cadenas

El comportamiento de la comparación de cadenas en Microsoft SQL depende de la intercalación establecida en la base de datos o en la columna.

Distinción entre mayúsculas y minúsculas

La comparación de cadenas SQL de Microsoft depende de la recopilación. Por defecto, la mayoría de las bases de datos utilizan una intercalación que no distingue entre mayúsculas y minúsculas, pero puedes anular este comportamiento con la 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"})

Comparación de patrones con LIKE

Use el operador LIKE con caracteres comodín para buscar patrones de texto; escape los caracteres especiales con notación entre corchetes para que coincidan con caracteres literales.

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

Consideraciones de codificación

El comportamiento de codificación depende del tipo de columna SQL de Microsoft y de la clasificación de la fuente.

Supuestos de codificación y valores predeterminados de Unicode

El mssql-python controlador gestiona la codificación automáticamente según el tipo de columna SQL de Microsoft. Por defecto, los parámetros de la cadena se envían como UTF-16LE para las columnas nvarchar y según la clasificación de la base de datos para las columnas varchar:

Tipo de columna Codificación por cable Resultado en Python
nvarchar, nchar, ntext UTF-16LE str (descifrado por el conductor)
varchar, char, text Codificación de bases de datos o de columnas str (decodificado por el controlador usando la codificación fuente)

Las cadenas de Python siempre son Unicode internamente. Cuando pasas un str parámetro, el controlador lo codifica para el tipo de columna objetivo. Por defecto, el controlador envía los parámetros de cadena como nvarchar (Unicode), lo que garantiza que los caracteres se conserven independientemente de la clasificación de la base de datos. En las columnas varchar, UTF-8 solo se aplica cuando la base de datos o la columna usa una intercalación compatible con UTF-8.

Si tu columna es varchar y necesitas enviar datos no Unicode para que coincidan exactamente con el tipo de columna (por ejemplo, para evitar advertencias de conversión implícitas), úsalo setinputsizes() para anular el valor por defecto:

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 la mayoría de las aplicaciones, el comportamiento por defecto es el correcto. Anula solo cuando veas advertencias de conversión implícitas en los planes de consulta o necesites coincidir con una clasificación específica varchar .

Codificación de conexión

El controlador mssql-python gestiona automáticamente la codificación de la conexión según la versión y configuración de Microsoft SQL Server. Como las cadenas de Python son Unicode, el controlador las codifica adecuadamente (UTF-8 o UTF-16) para el tipo de dato objetivo. No necesitas configurar la codificación de conexiones manualmente.

Columnas VARCHAR con colaciones heredadas

Las bases de datos con colaciones de Windows-1252 (CP1252), como Latin1_General_CI_AS, almacenan caracteres latinos extendidos (por ejemplo, , , y caracteres acentuados) en varchar columnas usando la codificación CP1252. El controlador decodifica correctamente estos caracteres en todas las plataformas.

Esta diferencia es importante para despliegues multiplataforma: los mismos varchar datos que se leen correctamente en Windows también se leen correctamente en Linux, sin necesidad de configuración 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

Si tu esquema lo permite, migrar varchar columnas a nvarchar evita por completo la ambigüedad de codificación y soporta todos los caracteres Unicode.

Codificación de archivos

Al leer archivos para insertar en la base de datos, especifica la codificación adecuada para preservar el contenido 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()

Operaciones comunes con cadenas

Estos ejemplos cubren patrones comunes de manipulación de cadenas tanto en Python como en SQL.

Concatenación

Puedes concatenar cadenas ya sea en Python antes de insertarlas o usando los operadores de cadenas de SQL en el 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
""")

Formato de cadena

Aplica el formato en Python para mostrar cadenas con moneda, relleno o alineación antes de mostrarlas a los usuarios.

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 frente a cadena vacía

Microsoft SQL trata NULL y cadena vacía ('') como valores diferentes. NULL significa "desconocido" mientras que cadena vacía significa "conocida por estar vacía". Elige una convención para tu solicitud y sé consistente. La mayoría de las aplicaciones usan NULL para los campos opcionales que faltan.

El siguiente ejemplo demuestra cómo distinguir entre cadena NULL y cadena vacía:

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

Operaciones de recorte

Utiliza los métodos de cadena de Python para eliminar los espacios en blanco iniciales, finales o ambos de los valores recuperados de la base de datos.

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

Datos de cadenas JSON

Almacena documentos JSON en columnas nvarchar(max) y consulta con las funciones JSON de Microsoft SQL.

Almacenar JSON como nvarchar

Serializar diccionarios de Python en cadenas JSON e insertarlos en columnas nvarchar; recuperarlos y desserializarlos de nuevo en 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'

Utiliza funciones JSON SQL de Microsoft

Utiliza las funciones JSON de Microsoft SQL para analizar y filtrar los datos JSON directamente en consultas en lugar de en el código 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'

Úsalo LIKE para la comparación de patrones, o activa un índice de texto completo para búsquedas de texto más avanzadas.

Consultas en texto completo

El LIKE operador con patrones comodines ofrece una alternativa directa a la búsqueda en texto completo cuando no hay un índice completo disponible.

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

procedimientos recomendados

Aplica estas directrices para manejar correctamente los datos de cadenas en lenguajes y codificaciones.

Utiliza nvarchar para datos internacionales

Si no estás seguro de si una columna puede contener Unicode, usa nvarchar. El coste de almacenamiento es modesto y evita la pérdida de datos por conversión de caracteres.

El siguiente ejemplo muestra la diferencia entre definir columnas para datos Unicode y 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)
);

Validar la longitud de la cadena

Comprueba la longitud de la cadena en Python antes de insertarla para evitar errores de truncamiento y proporcionar mensajes de error significativos a los usuarios.

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

Maneja las cadenas binarias por separado

Distingue entre cadenas de texto (Pythonstr, SQLnvarchar) y datos binarios (Pythonbytes, SQLvarbinary) para evitar problemas de codificación.

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