Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Por defecto, el controlador mssql-python convierte los tipos de datos de Microsoft SQL en tipos Python apropiados (véase Mapeos de tipos de datos). El sistema de convertidor de salida permite anular este comportamiento para tipos SQL específicos, permitiendo:
- Transformaciones de tipo personalizadas (por ejemplo, de money a float en lugar de Decimal)
- Integración con bibliotecas de terceros (por ejemplo, datos espaciales para objetos Shapely)
- Formato específico de la empresa (por ejemplo, convertir fechas en formatos de cadena personalizados)
Registrar convertidores de salida
Úsalo add_output_converter() para registrar una función convertidora. La clave puede ser un tipo de Python que coincida con el type_code de cursor.description, o bien un código entero de tipo SQL de ODBC como mssql_python.SQL_DECIMAL:
import mssql_python
from datetime import datetime
def my_datetime_converter(value):
"""Convert datetime to a custom display string."""
if value is None:
return None
return value.strftime("%B %d, %Y at %I:%M %p")
conn = mssql_python.connect(connection_string)
conn.add_output_converter(datetime, my_datetime_converter)
cursor = conn.cursor()
cursor.execute("SELECT CAST('2026-07-16T10:30:00' AS DATETIME2)")
print(cursor.fetchval()) # July 16, 2026 at 10:30 AM
Elige entre una clave de tipo Python y una clave de código tipo SQL
Las dos formas clave difieren en la precisión con la que seleccionan las columnas:
Una clave de tipo Python se aplica a cada tipo SQL que se corresponde a ese tipo Python. Registrar un convertidor para
Decimaltransforma las columnas decimal, numeric, money y smallmoney.Una clave de código entero del tipo SQL coincide con el código de tipo ODBC que devuelve la columna, que es más específico, pero no siempre corresponde a un único tipo SQL.
SQL_NUMERICtransforma solo columnas numéricas .SQL_DECIMALTransforma las columnas decimal, dinero y smallmoney , porque las tres reportan ese mismo código.
Usa una clave entera cuando una clave tipo Python es más amplia de lo que quieres, o cuando portes código desde pyodbc, que usa claves enteras. Usa una clave de tipo Python cuando quieras que un convertidor cubra toda una familia de tipos SQL.
import mssql_python
def tag_decimal(value):
return f"D:{value}"
conn.add_output_converter(mssql_python.SQL_DECIMAL, tag_decimal)
cursor.execute("""
SELECT CAST(12.34 AS decimal(10,2)),
CAST(56.78 AS numeric(10,2)),
CAST(90.12 AS money)
""")
print(cursor.fetchone()) # ('D:12.34', Decimal('56.78'), 'D:90.1200')
Orden de resolución del convertidor
Para cada columna, el controlador selecciona como máximo un convertidor, en este orden:
El convertidor registrado para el código de tipo SQL ODBC de entero de la columna.
El convertidor registrado para el tipo Python en
cursor.description.El convertidor se registró para
SQL_WVARCHAR, pero solo cuando el tipo Python de la columna esstrobytes.
Firma de función convertidora
Al registrar un convertidor con una clave de tipo de Python, el tipo que pasas a add_output_converter() debe coincidir con uno de los siguientes tipos de Python. La columna Converter receives muestra lo que el controlador envía a tu función:
cursor.description type_code |
El convertidor recibe | Tipos SQL de Microsoft |
|---|---|---|
int |
int |
tinyint, smallint, , int, bigint |
float |
float |
real, float |
decimal.Decimal |
Decimal |
decimal, numeric, , money, smallmoney |
str |
bytes (codificado en UTF-16LE) |
char, varchar, nchar, nvarchar, xml |
datetime.datetime |
datetime |
datetime, datetime2, smalldatetime |
datetime.date |
date |
date |
bytes |
bytes |
varbinary, binary, , geography, geometry |
bool |
bool |
bit |
Note
Los convertidores de tipo cadena (clave str) reciben bytes sin procesar en codificación UTF-16LE, no cadenas de Python decodificadas. Todos los demás tipos reciben el objeto Python ya convertido.
from decimal import Decimal
def decimal_converter(value: Decimal | None) -> float | None:
if value is None:
return None
# value is a Decimal object for numeric/money types
return float(value)
Gestionar convertidores
Utiliza estos métodos para inspeccionar o eliminar convertidores ya registrados en una conexión.
Obtén el conversor existente
Recuperar la función de convertidor actualmente registrada para un tipo:
converter = conn.get_output_converter(str)
if converter:
print(f"Converter registered: {converter}")
else:
print("Using default conversion")
Quita un convertidor
Desregistra un convertidor para que el controlador vuelva a la conversión predeterminada para ese tipo:
conn.remove_output_converter(str)
# str columns now use default conversion
Borrar todos los convertidores
Restablece todos los convertidores registrados para usar los mapeos de tipos por defecto de Microsoft SQL:
conn.clear_output_converters()
# All types now use default conversion
Patrones comunes de convertidores
Estos ejemplos muestran las funciones convertidoras más necesarias.
Convertir VARCHAR en mayúsculas
Los convertidores de tipo cadena reciben bytes en bruto en la codificación UTF-16LE:
def uppercase_converter(value):
if value is None:
return None
return value.decode('utf-16-le').upper()
conn.add_output_converter(str, uppercase_converter)
cursor = conn.cursor()
cursor.execute("SELECT 'hello world'")
print(cursor.fetchval()) # HELLO WORLD
Convertir money a float
Por defecto, el controlador devuelve los tipos DECIMAL/NUMÉRICO como decimal.Decimal. Convertir a float:
from decimal import Decimal
def money_to_float(value):
if value is None:
return None
return float(value)
conn.add_output_converter(Decimal, money_to_float)
cursor = conn.cursor()
cursor.execute("SELECT CAST(19.99 AS MONEY)")
result = cursor.fetchval()
print(type(result)) # <class 'float'>
Analizar datos JSON
Microsoft SQL Server puede almacenar JSON como texto. Analizar automáticamente en objetos de Python:
import json
def json_converter(value):
if value is None:
return None
text = value.decode('utf-16-le')
try:
return json.loads(text)
except json.JSONDecodeError:
return text # Return as string if not valid JSON
conn.add_output_converter(str, json_converter)
cursor = conn.cursor()
cursor.execute("SELECT '{\"name\": \"Widget\", \"price\": 19.99}'")
data = cursor.fetchval()
print(data['name']) # Widget
Caution
Este ejemplo convierte TODOS los valores de cadena (nvarchar, varchar, xml). Se intenta analizar JSON en cada columna de cadenas. En la práctica, aplica el análisis sintáctico JSON de forma selectiva en lugar de como un convertidor general.
Formato personalizado de fecha y hora
Convertir la fecha y hora a formato de cadena ISO:
from datetime import datetime
def datetime_to_iso(value):
if value is None:
return None
return value.isoformat()
conn.add_output_converter(datetime, datetime_to_iso)
cursor = conn.cursor()
cursor.execute("SELECT TOP 1 ModifiedDate FROM Production.Product")
print(cursor.fetchval()) # 2014-02-08T10:01:36.827000
Consideraciones de seguridad
Caution
Cuando registras un convertidor de salida, la función que proporcionas se ejecuta sobre cada valor de base de datos correspondiente.
- Registra únicamente convertidores procedentes de código de confianza.
- Nunca aceptes funciones de convertidor a partir de la entrada del usuario.
- Los convertidores maliciosos podían ejecutar código arbitrario o filtrar datos.
# DANGEROUS - never do this
def unsafe_example(user_converter_code):
converter_func = eval(user_converter_code) # Security risk!
conn.add_output_converter(str, converter_func)
Ejemplo: Integración de datos espaciales
Integra los datos espaciales con la biblioteca Shapely:
from shapely import wkb
def geometry_converter(value):
"""Convert WKB binary to Shapely geometry object."""
if value is None:
return None
return wkb.loads(value)
conn.add_output_converter(bytes, geometry_converter)
# Query must use STAsBinary() to get standard WKB format
cursor = conn.cursor()
cursor.execute(
"SELECT SpatialLocation.STAsBinary() FROM Person.Address "
"WHERE SpatialLocation IS NOT NULL AND City = 'Seattle'"
)
for row in cursor:
shape = row[0]
print(f"Point: ({shape.x:.4f}, {shape.y:.4f})")
Note
Registrar un bytes convertidor afecta a TODAS las columnas binarias (varbinaria, geografía, geometría). Usa clear_output_converters() después de las consultas espaciales si también consultas datos binarios no espaciales.
Ejemplo: Formato de fila personalizado
Crea un convertidor de clases de datos:
from dataclasses import dataclass
@dataclass
class Product:
id: int
name: str
price: float
def fetch_products_as_dataclass(cursor):
"""Convert rows to Product dataclass instances."""
cursor.execute(
"SELECT ProductID, Name, ListPrice FROM Production.Product "
"WHERE ListPrice > 0"
)
results = []
for row in cursor:
results.append(Product(
id=row.ProductID,
name=row.Name,
price=float(row.ListPrice)
))
return results
products = fetch_products_as_dataclass(cursor)
for p in products[:5]:
print(f"{p.name}: ${p.price:.2f}")
Alcance y vida útil del convertidor
- De ámbito por conexión: el controlador registra los convertidores para cada conexión, no de forma global.
- Persistente: Los conversores permanecen activos hasta que los quitas o cierras la conexión.
- No heredadas: Las conexiones nuevas no heredan conversores de otras conexiones.
conn1 = mssql_python.connect(connection_string)
conn1.add_output_converter(str, uppercase_converter)
conn2 = mssql_python.connect(connection_string)
# conn2 does NOT have the converter registered
Consideraciones sobre el rendimiento
- El controlador invoca los convertidores para cada valor del tipo registrado.
- Mantén las funciones del convertidor eficientes.
- Para consultas de alto volumen, considera si es necesaria la conversión.
- Analice el rendimiento si los convertidores afectan a la capacidad de procesamiento.
import time
def slow_converter(value):
time.sleep(1) # Avoid time consuming routines like this - adds 1 second for each value processed!
return str(value) if value else None