Resolução de problemas de mssql-python

Diagnostice e resolva problemas comuns ao utilizar o driver mssql-python para se ligar ao SQL Server, Base de Dados SQL do Azure, Azure SQL Managed Instance e base de dados SQL no Microsoft Fabric.

Problemas de instalação

o pip install falha ou compila a partir do código-fonte

Sintomas:

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

Possíveis causas e soluções:

  • Não há volante pré-montado para a tua plataforma

    • Verifica se estás a usar uma versão de Python suportada (versões 3.10 e posteriores) e uma plataforma. Consulte ciclo de vida do suporte para a matriz de compatibilidade. Atualize o pip antes de instalar com pip install --upgrade pip. Para ambientes de equipa reproduzíveis, utilize o workflow bloqueado em Implementações reproduzíveis ou os padrões de contentores em Contentores e desenvolvimento local para reduzir a divergência das máquinas locais.
  • Ambiente virtual não ativado

    • Ative primeiro o seu ambiente virtual. Instalar Python no sistema pode causar erros ou conflitos de permissões.
    python -m venv .venv
    .venv\Scripts\activate
    pip install mssql-python
    

Instalações de drivers conflitantes

Sintomas:

Erros de importação ou comportamentos inesperados após instalar mssql-python ao lado pyodbc no mesmo ambiente.

Correção:

mssql-python e pyodbc podem coexistir. Se vir conflitos, crie um ambiente virtual limpo:

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

Problemas de conexão

Impossível ligar-se ao servidor

Sintomas:

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

Possíveis causas e soluções:

  • Servidor inacessível

    • Verifica que o nome do servidor e a porta estão corretos.
    • Verifique a conectividade da rede: ping servername ou telnet servername 1433.
    • Garante que o firewall permite ligações de saída na porta 1433.
  • SQL Server não está a correr

    • Verifique se o serviço SQL Server foi iniciado.
    • Para instâncias nomeadas, verifique se o serviço SQL Server Browser está a funcionar.
  • Regras de firewall do SQL do Azure

    • Adicione o IP do seu cliente às regras do firewall SQL do Azure no portal Azure.
    • Para Azure SQL Managed Instance, certifique-se de que está a ligar-se a partir de uma rede 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}")

Início de sessão falhado

Sintomas:

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

Possíveis causas e soluções:

  • Incompatibilidade dos modos de autenticação

    • Para Base de Dados SQL do Azure, Azure SQL Managed Instance e a base de dados SQL no Fabric, privilegie um modo do Microsoft Entra, como Authentication=ActiveDirectoryDefault.
    • Se estiveres a usar autenticação SQL intencionalmente, verifica se o servidor a permite e se estás a usar o formato de login correto para esse endpoint.
  • Credenciais de autenticação SQL incorretas

    • Verifique nome de utilizador e palavra-passe.
    • Para SQL do Azure, inclua o nome de utilizador completo: username@servername.
  • O utilizador não existe na base de dados

    • Verifique se o utilizador tem acesso à base de dados especificada.
    • Verifique se o início de sessão está mapeado para um utilizador da base de dados.
  • Autenticação não configurada

    • Utilize a autenticação do Microsoft Entra (recomendada): Authentication=ActiveDirectoryDefault.
    • Se estiveres a resolver problemas num SQL Server local que deveria aceitar autenticação SQL, verifica se o SQL Server usa autenticação em modo misto.

Tempo limite de ligação

Sintomas:

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

Possíveis causas e soluções:

  • O servidor demora a responder

    • Aumentar o tempo de espera da ligação:
    conn = mssql_python.connect(connection_string, timeout=60)
    
  • Latência da rede

    • Verifique o caminho da rede até ao servidor.
    • Considera usar um caminho de rede mais curto ou VPN.
  • Servidor sob carga elevada

    • Tenta ligar-te durante as horas de menor afluência.
    • Contacte o administrador da sua base de dados.

Erros de certificado SSL

Sintomas:

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

Soluções:

Primeiro, prefira um certificado de confiança ou os padrões de desenvolvimento local em Container e desenvolvimento local. Usa TrustServerCertificate=yes apenas para desenvolvimento local contra um servidor que controlas.

Para desenvolvimento e testes com um certificado auto-assinado:

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

Atenção

TrustServerCertificate=yes é uma alternativa de recurso exclusivamente local. Não a leves para devcontainers partilhados, pipelines de CI ou implementações para produção. Para orientações mais abrangentes, consulte Encriptação e certificados.

Em produção, certifique-se de que os certificados adequados estão instalados e utilize:

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

Problemas de execução de consultas

Tabela ou objeto não encontrado

Sintomas:

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

Possíveis causas e soluções:

  • Contexto errado da base de dados

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

    # Use fully qualified name
    cursor.execute("SELECT * FROM dbo.TableName")
    
  • A tabela não existe

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

Erro de sintaxe

Sintomas:

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

Soluções:

  1. Teste primeiro o SQL no SSMS para verificar a sintaxe

  2. Verificar o escape de cadeias de caracteres - use consultas com parâmetros:

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

Erros de parâmetros

Sintomas:

ProgrammingError: [07001] Wrong number of parameters

Soluções:

  1. Conte os marcadores de posição e os parâmetros - devem corresponder

  2. Escolha o estilo de parâmetros correto:

    # 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 de tipo de dados

Erros de conversão de data e hora

Sintomas:

DataError: [22007] Invalid datetime format

Soluções:

Use objetos data-hora em Python em vez de cadeias de caracteres:

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

Questões de precisão decimal

Sintomas:

Os números parecem truncados ou arredondados incorretamente.

Soluções:

Utilização 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 codificação Unicode

Sintomas:

Caracteres especiais aparecem distorcidos ou causam erros.

Soluções:

  1. Utilize colunas NVARCHAR para dados Unicode na base de dados

  2. Passe as cadeias diretamente - o driver trata da codificação:

    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 desempenho

Execução lenta da consulta

Possíveis causas e soluções:

  • Índices em falta: Verifique o plano de execução da consulta no SSMS.

  • Grandes conjuntos de resultados: Use fetchmany() em vez de fetchall():

    cursor.arraysize = 1000
    while True:
         rows = cursor.fetchmany()
         if not rows:
             break
         process_rows(rows)
    
  • Agregação de ligações desativada: Ativar agregação:

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

Problemas de memória com resultados de grande dimensão

Sintomas:

O processo Python fica sem memória.

Soluções:

  1. Resultados do fluxo em vez de carregar tudo na memória:

    cursor.execute("SELECT * FROM LargeTable")
    for row in cursor:  # Iterates one row at a time
        process_row(row)
    
  2. Utilize a paginação no 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
    

Questões de transação

Âmbito da tabela temporária com autocommit

As tabelas temporárias (#tablename) criadas dentro de uma transação desaparecem quando a transação é revertida. Esta é uma fonte comum de confusão quando o autocommit está desligado (o padrão):

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

Correção: Faça commit imediatamente após criar uma tabela temporária ou use o modo de confirmação 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()

As instruções DDL que exigem modo de autocommit, como CREATE DATABASE, falham dentro de uma transação aberta. Defina a confirmação automática antes de os executar:

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

Transação não comprometida

Sintomas:

As alterações nos dados não persistem após o fecho da ligação.

Solution:

Com autocommit=False (predefinição), tem de chamar 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!

Ou utilize o modo autocommit:

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

Erros de bloqueio

Sintomas:

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

Solution:

A lógica de repetição (ver lógica de repetição) lida com a falha imediata, mas impasses recorrentes indicam um problema de conceção. Para corrigir a causa raiz, capture o gráfico de deadlock e analise quais as instruções e tipos de bloqueio envolvidos. Correções comuns incluem reordenar operações para que transações concorrentes adquiram bloqueios na mesma sequência, reduzir o âmbito das transações e adicionar índices apropriados para diminuir a duração dos bloqueios.

Para uma explicação detalhada da análise de deadlocks, consulte o guia sobre Deadlocks. Se estiveres a usar o Base de Dados SQL do Azure, consulta Analisar e prevenir interbloqueios.

Problemas de carregamento em massa

Violação de restrições durante a cópia em massa

Sintomas:

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

Causa:

Os dados no seu lote violam as restrições das tabelas (chave primária, única, CHECK ou chave estrangeira).

Correção:

Valida os dados antes de carregar. Para conjuntos de dados de grande dimensão, carregue-os primeiro para uma tabela intermédia e, em seguida, faça a fusão com a tabela 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 padrões de upsert com tabelas de preparação, consulte Padrões de carregamento e movimentação de dados.

Erros de mapeamento de colunas

Sintomas:

RuntimeError: Bulk copy failure - column count mismatch

Causa:

O número de colunas nos seus dados não corresponde ao número de colunas da tabela alvo, ou as colunas estão na ordem errada.

Correção:

Certifique-se de que os seus dados correspondem exatamente ao esquema da tabela na ordem e no número de elementos:

# 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 a cópia em massa

Sintomas:

Os dados são carregados, mas os valores estão truncados, arredondados ou incorretos.

Causa:

Os valores de Python não correspondem de forma clara aos tipos de coluna de destino. Casos comuns: float valores carregados em decimal colunas (perda de precisão), ou cadeias sobredimensionadas carregadas em colunas de comprimento fixo.

Correção:

Use os tipos corretos de Python que correspondam ao seu 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)

Falhas na associação de tipos do NumPy

Sintomas:

Os parâmetros falham silenciosamente ou levantam erros de tipo de dados ao usar números inteiros ou tipos float.

Causa:

Tipos NumPy como numpy.int64 e numpy.int32 não são aceites por isinstance(x, int) no NumPy 2.x. A inferência de tipo do condutor não os reconhece, o que causa comportamentos inesperados.

Correção:

Converter valores numpy para tipos nativos de Python antes de fazer a ligação:

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 dados maiores, use os caminhos de integração Arrow ou pandas em vez disso, que tratam internamente da conversão de tipos.

Cópia em massa com tabelas temporárias

Sintomas:

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

Causa:

bulkcopy() Não é possível resolver tabelas temporárias de sessão (#tablename) devido a limitações de consulta de metadados. As tabelas temporárias globais (##tablename) e as tabelas permanentes funcionam.

Correção:

Use uma tabela temporária global ou uma tabela de preparação regular:

# 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 dados pequenos onde se prefere uma tabela temporária de sessão, use executemany() em vez disso:

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

Questões de contentores e CI

Bibliotecas de sistema em falta no Linux

Sintomas:

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

Correção:

Instale os pacotes de sistema necessários. Os pacotes diferem pela distribuição:

Distribution Comando de Instalação
Ubuntu / Debian sudo apt-get install libltdl7 libkrb5-3 libgssapi-krb5-2
Red Hat / Fedora sudo dnf install libtool-ltdl krb5-libs
Alpine apk add libltdl krb5-libs

Para exemplos de Dockerfile, veja Contentor e desenvolvimento local.

Erros SSL do macOS após a instalação

Sintomas:

Erros relacionados com SSL ao ligar a partir do macOS, especialmente no Apple Silicon.

Correção:

Instale o OpenSSL via Homebrew e defina as bandeiras do linker:

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

Ferramentas de diagnóstico

Ativar o registo do controlador

Utilize mssql_python.setup_logging() para ativar registos de DEBUG detalhados para diagnóstico de problemas. Todas as operações do driver são registadas, incluindo instruções SQL, parâmetros, operações ODBC internas e alterações no estado da ligação.

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

Os ficheiros de registo são escritos em formato CSV e rodam automaticamente a 512 MB com cinco backups. Dados sensíveis, como palavras-passe e tokens de acesso, são automaticamente higienizados na saída do log.

Para adicionar as suas próprias entradas de registo juntamente com os registos do 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

Atenção

O registo tem um impacto no desempenho. Ative-o apenas para resolução de problemas, não por predefinição em produção.

Obtenha informações sobre o condutor

Recupere a versão do driver e os dados do servidor de uma ligação ativa:

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

Verificar o estado da ligação

Teste se uma ligação ainda está aberta antes de tentar operações:

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

Referência rápida: Erros comuns

Erro SQLSTATE Causa comum Correção rápida
Cliente incapaz de estabelecer ligação 08001 Servidor inacessível Verifique o nome/porta do servidor
Início de sessão falhado 28000 Credenciais erradas Verificar nome de utilizador/palavra-passe
O tempo limite expirou HYT00/HYT01 Rede lenta Aumentar o tempo limite
Nome do objeto inválido 42S02 Tabela/esquema errado Use nomes totalmente qualificados
Erro de sintaxe 42000 Erro SQL Utilizar consultas parametrizadas
Violação de restrições 23000 Violação FK/PK Verificar a integridade dos dados
Impasse 40001 Contenção de bloqueio Tente novamente e, em seguida, analise o gráfico de interbloqueio