Solucionar problemas en mssql-python

Diagnostica y resuelve problemas comunes al usar el controlador mssql-python para conectarte a SQL Server, Azure SQL Database, Azure SQL Managed Instance y base de datos SQL en Microsoft Fabric.

Problemas de instalación

Fallos de instalación de PIP o compilación desde el código fuente

Síntomas:

error: Microsoft Visual C++ 14.0 or greater is required
ERROR: Failed building wheel for mssql-python

Posibles causas y soluciones:

  • No hay volante preensamblado para tu plataforma

    • Comprueba que tienes una versión de Python compatible (versiones 3.10 y posteriores) y una plataforma. Consulta el ciclo de vida del soporte para la matriz de compatibilidad. Actualiza pip antes de instalar con pip install --upgrade pip. Para entornos de equipo reproducibles, utiliza el flujo de trabajo fijado en Repeatable deployments o los patrones de contenedores de Container and local development para reducir la desviación en las máquinas locales.
  • Entorno virtual no activado

    • Activa primero tu entorno virtual. Instalar Python en el sistema puede causar errores de permisos o conflictos.
    python -m venv .venv
    .venv\Scripts\activate
    pip install mssql-python
    

  • Bibliotecas del sistema Linux que faltan

Instalaciones de controladores en conflicto

Síntomas:

Errores de importación o comportamientos inesperados tras instalarlos mssql-python junto pyodbc en el mismo entorno.

Corrección:

mssql-python y pyodbc pueden coexistir. Si ves conflictos, crea un entorno virtual limpio:

python -m venv .venv --clear
.venv\Scripts\activate
pip install mssql-python

Problemas de conexión

No se puede conectar al servidor

Síntomas:

OperationalError: [08001] (0) Client unable to establish connection

Posibles causas y soluciones:

  • Servidor no accesible

    • Verifica que el nombre del servidor y el puerto sean correctos.
    • Comprobar conectividad de red: ping servername o telnet servername 1433.
    • Asegúrate de que el cortafuegos permita conexiones salientes en el puerto 1433.
  • SQL Server no está en ejecución

    • Verifica que el servicio de SQL Server está activado.
    • Para instancias nombradas, verifica que el servicio SQL Server Browser esté en funcionamiento.
  • Reglas de firewall Azure SQL

    • Añade la IP de tu cliente a las reglas del firewall Azure SQL en el portal de Azure.
    • Para Azure SQL Managed Instance, asegúrate de conectarte desde una red permitida.
# Test basic connectivity
import socket
try:
    sock = socket.create_connection(("<server>.database.windows.net", 1433), timeout=5)
    print("TCP connection successful")
    sock.close()
except Exception as e:
    print(f"Cannot reach server: {e}")

Error de inicio de sesión

Síntomas:

OperationalError: [28000] (18456) Login failed for user 'username'.

Posibles causas y soluciones:

  • Incompatibilidad en el modo de autenticación

    • Para Azure SQL Database, Azure SQL Managed Instance y SQL Database en Fabric, se prefiere un modo de autenticación de Microsoft Entra como Authentication=ActiveDirectoryDefault.
    • Si usas autenticación SQL intencionadamente, verifica que el servidor lo permita y que usas el formato de inicio de sesión correcto para ese endpoint.
  • Credenciales de autenticación SQL incorrectas

    • Verifica el nombre de usuario y la contraseña.
    • Para Azure SQL, incluye el nombre de usuario completo: username@servername.
  • El usuario no existe en la base de datos

    • Verifica que el usuario tenga acceso a la base de datos especificada.
    • Comprueba si el inicio de sesión está asignado a un usuario de la base de datos.
  • Autenticación no configurada

    • Utiliza la autenticación de Microsoft Entra (recomendada): Authentication=ActiveDirectoryDefault.
    • Si estás solucionando problemas en un SQL Server local que debería aceptar autenticación SQL, verifica que SQL Server use autenticación en modo mixto.

Tiempo de espera de conexión

Síntomas:

OperationalError: [HYT00] (0) Timeout expired
OperationalError: [HYT01] (0) Connection timeout expired

Posibles causas y soluciones:

  • El servidor tarda en responder

    • Aumenta el tiempo de espera de conexión:
    conn = mssql_python.connect(connection_string, timeout=60)
    
  • Latencia de red

    • Comprueba la ruta de red hacia el servidor.
    • Considera usar una ruta de red o VPN más corta.
  • Servidor bajo alta carga

    • Prueba a conectarte en horas valle.
    • Contacta con el administrador de tu base de datos.

Errores de certificado SSL

Síntomas:

OperationalError: [08001] SSL Provider: The certificate chain was issued by an authority that is not trusted

Soluciones:

Primero, prefiera un certificado de confianza o los patrones de desarrollo local en Contenedor y desarrollo local. Úsalo TrustServerCertificate=yes solo para desarrollo local contra un servidor que controlas.

Para desarrollo y pruebas con un certificado autofirmado:

conn = mssql_python.connect(
    "Server=<server>.database.windows.net;"
    "Database=<database>;"
    "Authentication=ActiveDirectoryDefault;"
    "Encrypt=yes;"
    "TrustServerCertificate=yes;"  # Don't use in production
)

Caution

TrustServerCertificate=yes es un recurso de respaldo solo local. No lo lleves a devcontainers compartidos, pipelines de CI o despliegues en producción. Para una orientación más general, véase Cifrado y certificados.

Para la producción, asegúrese de que se instalen los certificados adecuados y se utilicen:

conn = mssql_python.connect(
    "Server=<server>.database.windows.net;"
    "Database=<database>;"
    "Authentication=ActiveDirectoryDefault;"
    "Encrypt=yes;"
    "HostnameInCertificate=<server>.domain.com;"
)

Problemas de ejecución de consultas

Tabla u objeto no encontrado

Síntomas:

ProgrammingError: [42S02] (208) Invalid object name 'TableName'.

Posibles causas y soluciones:

  • Contexto incorrecto de la base de datos

    # Ensure you're connected to the correct database
    cursor.execute("SELECT DB_NAME()")
    print(cursor.fetchone()[0])
    
  • Esquema no especificado

    # Use fully qualified name
    cursor.execute("SELECT * FROM dbo.TableName")
    
  • La tabla no existe

    # Check if table exists
    cursor.execute("""
         SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES 
         WHERE TABLE_NAME = 'TableName'
    """)
    

Error de sintaxis

Síntomas:

ProgrammingError: [42000] (102) Incorrect syntax near '...'.

Soluciones:

  1. Prueba primero SQL en SSMS para verificar la sintaxis

  2. Comprueba el escape de cadenas - usa consultas parametrizadas:

    # Wrong - vulnerable to syntax issues and SQL injection
    cursor.execute(f"SELECT * FROM Production.Product WHERE Name = '{name}'")
    
    # Correct - use parameters
    cursor.execute("SELECT * FROM Production.Product WHERE Name = %(name)s", {"name": name})
    

Errores de parámetros

Síntomas:

ProgrammingError: [07001] Wrong number of parameters

Soluciones:

  1. Cuenta los marcadores de posición y los parámetros - deben coincidir

  2. Elige el estilo de parámetro adecuado:

    # Qmark style - positional
    cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Name LIKE ?", (1, "Adjustable%"))
    print(cursor.fetchone())
    
    # Pyformat style - named
    cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(id)s AND Name LIKE %(name)s", {"id": 1, "name": "Adjustable%"})
    print(cursor.fetchone())
    

Problemas con el tipo de datos

Errores de conversión de fecha y hora

Síntomas:

DataError: [22007] Invalid datetime format

Soluciones:

Usa objetos de fecha-hora en Python en lugar de cadenas:

from datetime import datetime

cursor.execute("CREATE TABLE #Events (EventDate DATETIME)")

# Wrong - this raises an error for invalid dates
try:
    cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": "2024-13-45"})
except Exception as e:
    print(f"Expected error: {e}")

# Correct - use Python datetime objects
cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": datetime(2024, 3, 15)})
cursor.execute("SELECT EventDate FROM #Events")
print(cursor.fetchone())

Problemas de precisión decimal

Síntomas:

Los números aparecen truncados o redondeados incorrectamente.

Soluciones:

Uso decimal.Decimal para valores numéricos precisos:

from decimal import Decimal

cursor.execute("CREATE TABLE #PriceDemo (ListPrice DECIMAL(10,2))")
# Preserve full precision
cursor.execute(
    "INSERT INTO #PriceDemo (ListPrice) VALUES (%(list_price)s)",
    {"list_price": Decimal("19.99")}
)

Problemas de codificación Unicode

Síntomas:

Los caracteres especiales aparecen distorsionados o causan errores.

Soluciones:

  1. Utiliza columnas NVARCHAR para los datos Unicode en tu base de datos

  2. Pasa cadenas directamente : el controlador se encarga de la codificación:

    cursor.execute("CREATE TABLE #UnicodeDemo (Name NVARCHAR(50))")
    cursor.execute("INSERT INTO #UnicodeDemo (Name) VALUES (%(name)s)", {"name": "日本語"})
    cursor.execute("SELECT Name FROM #UnicodeDemo")
    print(cursor.fetchone())
    

Problemas de rendimiento

Ejecución lenta de consultas

Posibles causas y soluciones:

  • Índices faltantes: Revisa el plan de ejecución de consultas en SSMS.

  • Conjuntos de resultados grandes: Usar fetchmany() en lugar de fetchall():

    cursor.arraysize = 1000
    while True:
         rows = cursor.fetchmany()
         if not rows:
             break
         process_rows(rows)
    
  • Agrupación de conexiones desactivada: Habilitar agrupación:

    import mssql_python
    mssql_python.pooling(max_size=20, idle_timeout=300)
    

Problemas de memoria con resultados grandes

Síntomas:

El proceso de Python se queda sin memoria.

Soluciones:

  1. Resultados del flujo en lugar de cargarlo todo en memoria:

    cursor.execute("SELECT * FROM LargeTable")
    for row in cursor:  # Iterates one row at a time
        process_row(row)
    
  2. Utiliza la paginación en el servidor:

    page_size = 1000
    offset = 0
    while True:
        cursor.execute(
            "SELECT * FROM LargeTable ORDER BY ID "
            "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY",
            (offset, page_size)
        )
        rows = cursor.fetchall()
        if not rows:
            break
        process_rows(rows)
        offset += page_size
    

Problemas de transacciones

Alcance de tablas temporales con autocommit

Las tablas temporales (#tablename) creadas dentro de una transacción desaparecen cuando la transacción se revierte. Esta es una fuente común de confusión cuando el autocommit está apagado (el valor por defecto):

conn = mssql_python.connect(connection_string)  # autocommit=False by default
cursor = conn.cursor()

cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")

# If the connection rolls back (explicit or on error), #TempData disappears
conn.rollback()

# This fails: Invalid object name '#TempData'
cursor.execute("SELECT * FROM #TempData")

Solución: Confirma la transacción inmediatamente después de crear una tabla temporal, o usa el modo de confirmación automática:

cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
conn.commit()  # Lock in the table definition

cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
conn.commit()

Las sentencias DDL que requieren modo de compromiso automático, como CREATE DATABASE, fallan dentro de una transacción abierta. Establece el autocommit antes de ejecutarlos:

conn.autocommit = True
cursor.execute("CREATE DATABASE TestDB")
conn.autocommit = False

Transacción no comprometida

Síntomas:

Los cambios en los datos no persisten después de cerrar la conexión.

Solution:

Con autocommit=False (por defecto), debes llamar a commit():

cursor.execute("CREATE TABLE #Products (Name NVARCHAR(100))")
cursor.execute("INSERT INTO #Products (Name) VALUES (%(name)s)", {"name": "Widget"})
conn.commit()  # Don't forget this!

O usar modo de compromiso automático:

conn = mssql_python.connect(connection_string, autocommit=True)

Errores de interbloqueo

Síntomas:

OperationalError: [40001] (1205) Transaction ... was deadlocked on lock resources with another process

Solution:

La lógica de reintentos (véase lógica de reintentos) gestiona el fallo inmediato, pero los bloqueos recurrentes indican un problema de diseño. Para solucionar la causa raíz, captura el gráfico de bloqueo y analiza qué sentencias y tipos de bloqueo están implicados. Las soluciones comunes incluyen reordenar operaciones para que las transacciones competidoras adquieran bloqueos en la misma secuencia, reducir el alcance de la transacción y añadir índices apropiados para reducir la duración del bloqueo.

Para una guía completa del análisis de bloqueos, consulta la guía de bloqueos. Si usas Azure SQL Database, consulta Analizar y prevenir bloqueos.

Problemas de carga masiva

Infracciones de restricciones durante la copia masiva

Síntomas:

RuntimeError: CHECK constraint ... Conflict occurred in database ...
RuntimeError: Cannot insert duplicate key ... violation of PRIMARY KEY constraint

Causa:

Los datos de tu lote violan las restricciones de la tabla (clave primaria, única, CHECK o clave foránea).

Corrección:

Valida los datos antes de cargar. Para conjuntos de datos grandes, carga primero en una tabla intermedia y luego combina en la tabla de destino:

# Load into staging, then validate
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(100))")
cursor.bulkcopy("##Staging", rows)

# Check for duplicates before merging
cursor.execute("""
    SELECT s.ID FROM ##Staging s
    INNER JOIN dbo.Target t ON s.ID = t.ID
""")
dupes = cursor.fetchall()
if dupes:
    print(f"Skipping {len(dupes)} duplicate rows")

# Insert only non-duplicate rows
cursor.execute("""
    INSERT INTO dbo.Target (ID, Name)
    SELECT s.ID, s.Name FROM ##Staging s
    WHERE NOT EXISTS (SELECT 1 FROM dbo.Target t WHERE t.ID = s.ID)
""")
conn.commit()

Para patrones upsert con tablas de etapas, véase Patrones de carga y movimiento de datos.

Errores de mapeo de columnas

Síntomas:

RuntimeError: Bulk copy failure - column count mismatch

Causa:

El número de columnas en tus datos no coincide con el número de columnas de la tabla objetivo, o las columnas están en un orden incorrecto.

Corrección:

Asegúrate de que tus datos coincidan exactamente con el esquema de la tabla en orden y cantidad:

# Check the target table schema
cursor.execute("""
    SELECT COLUMN_NAME, DATA_TYPE
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = 'MyTable'
    ORDER BY ORDINAL_POSITION
""")
for col in cursor.fetchall():
    print(col)

# Match your data to the column order
rows = [
    (1, "Widget", Decimal("19.99")),  # Must match table column order
    (2, "Gadget", Decimal("29.99")),
]
cursor.bulkcopy("dbo.MyTable", rows)

Desajustes de tipo durante la copia masiva

Síntomas:

Los datos se cargan, pero los valores están truncados, redondeados o incorrectos.

Causa:

Los valores de Python no se corresponden claramente con los tipos de columna destino. Casos comunes: float valores cargados en decimal columnas (pérdida de precisión), o cadenas sobredimensionadas cargadas en columnas de longitud fija.

Corrección:

Utiliza los tipos de Python correctos que coincidan con tu esquema:

from decimal import Decimal

# Use Decimal for decimal/numeric columns, not float
rows = [
    (1, "Widget", Decimal("19.99")),  # Correct
    # (1, "Widget", 19.99),           # Avoid: float loses precision
]
cursor.bulkcopy("dbo.Products", rows)

Fallos de vinculación de tipos de NumPy

Síntomas:

Los parámetros fallan silenciosamente o generan errores de tipo de dato al usar números enteros o tipos float.

Causa:

Los tipos de NumPy como numpy.int64 y numpy.int32 no superan isinstance(x, int) en NumPy 2.x. La inferencia de tipo del conductor no los reconoce, lo que provoca un comportamiento inesperado.

Corrección:

Convierte los valores numpy a tipos nativos de Python antes de asignar:

import numpy as np

# Convert individual values
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(product_id)s", {"product_id": int(np.int64(42))})

# Convert DataFrame values
for _, row in df.iterrows():
    cursor.execute(
        "INSERT INTO #Orders (ProductID, Qty) VALUES (%(product_id)s, %(qty)s)",
        {"product_id": int(row["ProductID"]), "qty": int(row["Qty"])}
    )

Para conjuntos de datos más grandes, utiliza en su lugar las vías de integración de Arrow o pandas, que gestionan internamente la conversión de tipos.

Bulkcopy con tablas temporales

Síntomas:

cursor.bulkcopy("#TempTable", data) genera RuntimeError: Invalid object name '#TempTable'.

Causa:

bulkcopy() No puedo resolver tablas temporales de sesión (#tablename) debido a limitaciones de búsqueda de metadatos. Las tablas temporales globales (##tablename) y las tablas permanentes funcionan.

Corrección:

Utiliza una tabla temporal global o una tabla de ensayo normal:

# Global temp table (visible to all sessions, dropped when last session disconnects)
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("##Staging", rows)

# Or use a permanent staging table
cursor.execute("CREATE TABLE dbo.Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("dbo.Staging", rows)

Para conjuntos de datos pequeños donde se prefiere una tabla temporal de sesión, use executemany() en su lugar:

cursor.execute("CREATE TABLE #Staging (ID INT, Name NVARCHAR(50))")
cursor.executemany("INSERT INTO #Staging (ID, Name) VALUES (?, ?)", rows)

Problemas de contenedores y CI

Bibliotecas del sistema ausentes en Linux

Síntomas:

ImportError: libltdl.so.7: cannot open shared object file: No such file or directory
ImportError: libkrb5.so.3: cannot open shared object file

Corrección:

Instala los paquetes de sistema necesarios. Los paquetes difieren por distribución:

Distribution Comando Install
Ubuntu/Debian sudo apt-get install libltdl7 libkrb5-3 libgssapi-krb5-2
Red Hat / Fedora sudo dnf install libtool-ltdl krb5-libs
Alpino apk add libltdl krb5-libs

Para ejemplos de Dockerfile, véase Contenedores y desarrollo local.

Errores SSL de macOS tras la instalación

Síntomas:

Errores relacionados con SSL al conectarse desde macOS, especialmente en Apple Silicon.

Corrección:

Instala OpenSSL a través de Homebrew y establece las banderas de enlace:

brew install openssl
export LDFLAGS="-L/opt/homebrew/opt/openssl/lib"
export CPPFLAGS="-I/opt/homebrew/opt/openssl/include"

Herramientas de diagnóstico

Habilitar el registro del controlador

Úsalo mssql_python.setup_logging() para habilitar un registro DEBUG completo para la resolución de problemas. Todas las operaciones del controlador se registran, incluyendo sentencias SQL, parámetros, operaciones ODBC internas y cambios en el estado de la conexión.

import mssql_python

# Enable logging to file (default)
mssql_python.setup_logging()

# Output to stdout (useful for CI/CD and containers)
mssql_python.setup_logging(output='stdout')

# Output to both file and stdout
mssql_python.setup_logging(output='both')

# Custom log file path (must use .txt, .log, or .csv extension)
mssql_python.setup_logging(log_file_path="/var/log/myapp/mssql.log")

Los archivos de registro se escriben en formato CSV y rotan automáticamente a 512 MB con cinco copias de seguridad. Datos sensibles como contraseñas y tokens de acceso se desinfectan automáticamente en la salida del log.

Para añadir sus propias entradas de registro junto con los registros del controlador, use driver_logger:

from mssql_python.logging import driver_logger

mssql_python.setup_logging()

driver_logger.debug("[App] Starting data processing")
driver_logger.error("[App] Failed to process record")
# Your entries appear in the same file with the same format

Caution

El registro supone una sobrecarga de rendimiento. Actívala solo cuando estés resolviendo problemas, no en producción por defecto.

Obtener información del conductor

Recupera la versión del controlador y los detalles del servidor de una conexión activa:

import mssql_python

conn = mssql_python.connect(connection_string)

# Driver version
print(f"Version: {mssql_python.__version__}")

# Server information
print(f"Server name: {conn.getinfo(mssql_python.SQL_SERVER_NAME)}")
print(f"Database name: {conn.getinfo(mssql_python.SQL_DATABASE_NAME)}")

Comprobación del estado de conexión

Prueba si una conexión sigue abierta antes de intentar operar:

try:
    cursor = conn.cursor()
    cursor.execute("SELECT 1")
    print("Connection is open")
except mssql_python.Error:
    print("Connection is closed or broken")

Referencia rápida: Errores comunes

Error SQLSTATE Causa común Corrección rápida
El cliente no puede establecer la conexión 08001 Servidor inaccesible Comprobar nombre del servidor/puerto
Error de inicio de sesión 28000 Credenciales incorrectas Verifica nombre de usuario/contraseña
Se ha agotado el tiempo de espera HYT00/HYT01 Red lenta Aumentar tiempo de espera
Nombre de objeto no válido. 42S02 Tabla/esquema incorrecto Utiliza nombres plenamente cualificados
Error de sintaxis 42000 Error de SQL Uso de consultas con parámetros
Infracción de restricción 23000 Infracción FK/PK Comprobar la integridad de los datos
Deadlock 40001 Contención de bloqueo Vuelve a intentarlo y luego analiza el gráfico de bloqueo