Устранение неполадок mssql-python

Диагностика и устранение распространённых проблем при использовании драйвера mssql-python для подключения к SQL Server, База данных SQL Azure, Управляемый экземпляр SQL Azure и SQL Database в Microsoft Fabric.

Проблемы с установкой

Установка pip завершается с ошибкой или выполняется сборка из исходного кода

Симптомы:

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

Возможные причины и решения:

  • Нет готового колеса для вашей платформы

  • Виртуальная среда не активирована

    • Сначала активируйте виртуальную среду. Установка в систему Python может вызвать ошибки с правами или конфликты.
    python -m venv .venv
    .venv\Scripts\activate
    pip install mssql-python
    

  • Отсутствующие системные библиотеки Linux

Конфликтующие установки драйверов

Симптомы:

Ошибки импорта или неожиданное поведение после установки mssql-python вместе с pyodbc в одной и той же среде.

Исправление:

mssql-python и pyodbc могут сосуществовать. Если вы видите конфликты, создайте чистую виртуальную среду:

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

Проблемы с подключением

Не удаётся подключиться к серверу

Симптомы:

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

Возможные причины и решения:

  • Сервер недоступен

    • Проверьте, что имя сервера и порт верны.
    • Проверьте сетевое подключение: ping servername или telnet servername 1433.
    • Убедитесь, что файрвол позволяет исходящие соединения на порте 1433.
  • SQL Server не работает

    • Проверьте, запущен ли сервис SQL Server.
    • Для именованных экземпляров проверьте, работает ли сервис SQL Server Browser.
  • Azure SQL firewall rules

    • Добавьте свой клиентский IP в правила брандмауэра Azure SQL в портале Azure.
    • Для Управляемый экземпляр SQL Azure убедитесь, что вы подключаетесь из разрешённой сети.
# 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}")

Сбой входа

Симптомы:

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

Возможные причины и решения:

  • Несоответствие режимов аутентификации

    • Для База данных SQL Azure, Управляемый экземпляр SQL Azure и базы данных SQL в Fabric предпочтительно использовать режим Microsoft Entra, например Authentication=ActiveDirectoryDefault.
    • Если вы намеренно используете SQL-аутентификацию, убедитесь, что сервер это разрешает и что вы используете правильный формат входа для этой конечной точки.
  • Неправильные учетные данные SQL-аутентификации

    • Проверьте имя пользователя и пароль.
    • Для Azure SQL укажите полное имя пользователя: username@servername.
  • User не существует в базе данных

    • Проверьте, что пользователь имеет доступ к указанной базе данных.
    • Проверьте, связан ли вход с пользователем базы данных.
  • Аутентификация не настроена

    • Используйте аутентификацию Microsoft Entra (рекомендую): Authentication=ActiveDirectoryDefault.
    • Если вы ищете неполадки с локальным SQL Server, который должен поддерживать SQL-аутентификацию, проверьте, использует ли SQL Server смешанную аутентификацию.

Время соединения истекло

Симптомы:

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

Возможные причины и решения:

  • Сервер медленно отвечает

    • Увеличьте тайм-аут соединения:
    conn = mssql_python.connect(connection_string, timeout=60)
    
  • Задержка сети

    • Проверьте путь сети к серверу.
    • Рассмотрите возможность использования более короткого сетевого пути или VPN.
  • Сервер под большой нагрузкой

    • Попробуйте подключиться в непиковые часы.
    • Свяжитесь с администратором вашей базы данных.

Ошибки SSL-сертификата

Симптомы:

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

Решения:

Во-первых, предпочитайте проверенный сертификат или локальные паттерны разработки в контейнерной и локальной разработке. Используйте TrustServerCertificate=yes только для локальной разработки при работе с сервером, который вы контролируете.

Для разработки и тестирования с самоподписанным сертификатом:

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

Предостережение

TrustServerCertificate=yes — это резервный вариант, используемый только локально. Не переносите это в общие контейнеры разработки, CI-конвейеры или производственные развертывания. Для более широких рекомендаций см. раздел Шифрование и сертификаты.

Для производства убедитесь, что установлены соответствующие сертификаты и используйте:

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

Проблемы с выполнением запросов

Таблица или объект не найден.

Симптомы:

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

Возможные причины и решения:

  • Неправильный контекст базы данных

    # Ensure you're connected to the correct database
    cursor.execute("SELECT DB_NAME()")
    print(cursor.fetchone()[0])
    
  • Схема не указана

    # Use fully qualified name
    cursor.execute("SELECT * FROM dbo.TableName")
    
  • Таблицы не существует

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

Синтаксическая ошибка

Симптомы:

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

Решения:

  1. Сначала проверьте SQL в SSMS для проверки синтаксиса

  2. Проверьте выход строк — используйте параметризованные запросы:

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

Ошибки параметров

Симптомы:

ProgrammingError: [07001] Wrong number of parameters

Решения:

  1. Подсчитайте заполнители и параметры — их количество должно совпадать

  2. Выберите правильный стиль параметров:

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

Проблемы с типами данных

Ошибки конвертации времени и даты

Симптомы:

DataError: [22007] Invalid datetime format

Решения:

Используйте объекты datetime в Python вместо строк:

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

Проблемы с десятичной точностью

Симптомы:

Числа выглядят усечёнными или округлёнными неправильно.

Решения:

Использование decimal.Decimal для точных числовых значений:

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

Проблемы с кодированием Unicode

Симптомы:

Особые символы выглядят искажёнными или вызывают ошибки.

Решения:

  1. Используйте столбцы NVARCHAR для данных Unicode в вашей базе данных

  2. Передавайте строки напрямую — драйвер занимается кодированием:

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

Проблемы с производительностью

Медленное выполнение запросов

Возможные причины и решения:

  • Отсутствующие индексы: проверьте план выполнения запросов в SSMS.

  • Большие наборы результатов: используйте fetchmany() вместо fetchall():

    cursor.arraysize = 1000
    while True:
         rows = cursor.fetchmany()
         if not rows:
             break
         process_rows(rows)
    
  • Пул соединений отключен: Включите пул соединений:

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

Проблемы с памятью при больших результатах

Симптомы:

Процессу Python не хватает памяти.

Решения:

  1. Результаты потока вместо того, чтобы загружать всё в память:

    cursor.execute("SELECT * FROM LargeTable")
    for row in cursor:  # Iterates one row at a time
        process_row(row)
    
  2. Используйте страницирование на стороне сервера:

    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
    

Вопросы транзакций

Область видимости временной таблицы при автокоммите

Временные таблицы (#tablename), созданные внутри транзакции, исчезают при откате транзакции. Это часто вызывает путаницу, когда автокоммит отключён (по умолчанию):

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

Исправление: Зафиксируйте транзакцию сразу после создания временной таблицы или используйте режим автокоммита:

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

DDL-операторы, требующие режима автофиксации, например CREATE DATABASE, не работают внутри открытой транзакции. Настройте автокоммит перед их запуском:

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

Сделка не была совершена

Симптомы:

Изменения данных не сохраняются после закрытия соединения.

Solution:

С autocommit=False (по умолчанию) вы должны вызвать 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!

Или используйте режим автокоммита:

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

Ошибки взаимоблокировки

Симптомы:

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

Solution:

Повторная логика (см. логику повторного попробовки) обрабатывает немедленный сбой, но повторяющиеся тупики указывают на проблему проектирования. Чтобы устранить коренную причину, зафиксируйте граф тупиков и проанализируйте, какие операторы и типы замков задействованы. Распространённые способы устранения включают изменение порядка операций так, чтобы конкурирующие транзакции получали блокировки в одной и той же последовательности, сокращение области транзакции и добавление подходящих индексов для уменьшения времени удержания блокировок.

Для полного обзора анализа тупиков смотрите руководство по Deadlocks. Если вы используете База данных SQL Azure, см. Анализ и предотвращение взаимоблокировок.

Проблемы с массовой нагрузкой

Нарушения ограничений во время массового копирования

Симптомы:

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

Причина:

Данные в вашем пакете нарушают ограничения таблицы (первичный ключ, уникальный, CHECK или внешний ключ).

Исправление:

Проверьте данные перед загрузкой. Для больших наборов данных сначала загрузите их в промежуточную таблицу, а затем выполните слияние с целевой таблицей:

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

Для сценариев upsert с промежуточными таблицами см. Шаблоны загрузки и перемещения данных.

Ошибки отображения столбцов

Симптомы:

RuntimeError: Bulk copy failure - column count mismatch

Причина:

Количество столбцов в ваших данных не совпадает с количеством столбцов целевой таблицы, или столбцы расположены в неправильном порядке.

Исправление:

Убедитесь, что ваши данные точно соответствуют схеме таблицы по порядку и количеству:

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

Несоответствия типов при массовом копировании

Симптомы:

Данные загружаются, но значения усечаны, округлены или некорректны.

Причина:

Значения Python не могут быть однозначно сопоставлены с типами целевого столбца. Распространённые случаи: значения float, загруженные в столбцы decimal (потеря точности), или слишком длинные строки, загруженные в столбцы фиксированной длины.

Исправление:

Используйте правильные типы Python, соответствующие вашей схеме:

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)

Ошибки привязки типов NumPy

Симптомы:

При использовании целочисленных типов или типов с плавающей точкой NumPy параметры либо молча не срабатывают, либо вызывают ошибки типа данных.

Причина:

Типы NumPy, такие как numpy.int64 и numpy.int32, не проходят isinstance(x, int) в NumPy 2.x. Механизм вывода типов драйвера не распознаёт их, из-за чего возникает непредсказуемое поведение.

Исправление:

Преобразуйте значения numpy в родные типы Python перед привязкой:

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

Для больших наборов данных используйте пути интеграции Arrow или pandas , которые выполняют внутреннее преобразование типов.

Массовое копирование с временными таблицами

Симптомы:

cursor.bulkcopy("#TempTable", data) вызывает RuntimeError: Invalid object name '#TempTable'.

Причина:

bulkcopy() не может обрабатывать временные таблицы сеанса (#tablename) из-за ограничений при поиске метаданных. Глобальные временные таблицы (##tablename) и постоянные таблицы работают.

Исправление:

Используйте глобальную временную таблицу или обычную таблицу стадирования:

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

Для небольших наборов данных, где предпочтительнее временная таблица сессий, используйте executemany() вместо этого:

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

Вопросы контейнера и CI

Отсутствующие системные библиотеки в Linux

Симптомы:

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

Исправление:

Установите необходимые системные пакеты. Пакеты различаются по распределению:

Distribution Команда установки
Ubuntu / Debian sudo apt-get install libltdl7 libkrb5-3 libgssapi-krb5-2
Red Hat / Fedora sudo dnf install libtool-ltdl krb5-libs
Алпайн apk add libltdl krb5-libs

Для примеров Dockerfile см. раздел Container and local development.

Ошибки macOS SSL после установки

Симптомы:

Ошибки, связанные с SSL, при подключении с macOS, особенно на Apple Silicon.

Исправление:

Установите OpenSSL через Homebrew и установите флаги linker:

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

Средства диагностики

Включить логирование драйверов

Используйте mssql_python.setup_logging() для включения комплексного DEBUG-логирования для устранения неполадок. Все операции драйвера регистрируются в журнале, включая операторы SQL, параметры, внутренние операции ODBC и изменения состояния соединения.

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

Файлы журналов записываются в формате CSV, и при достижении 512 МБ для них автоматически выполняется ротация с сохранением пяти резервных копий. Конфиденциальные данные, такие как пароли и токены доступа, автоматически очищаются в выходе журналов.

Чтобы добавить свои записи в журнале вместе с логами водителей, используйте 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

Предостережение

Логирование связано с накладными расходами по производительности. Включайте его только при устранении неполадок, а не в продакшене по умолчанию.

Получите информацию о водителях

Получите версию драйвера и данные сервера из активного соединения:

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

Проверка состояния подключения

Проверьте, остаётся ли соединение открытым, прежде чем выполнять операции:

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

Краткая справка: Распространённые ошибки

Ошибка SQLSTATE Распространенная причина Быстрое исправление
Клиенту не удается установить подключение 08001 Сервер недоступен Проверьте имя/порт сервера
Сбой входа 28000 Неправильные удостоверения Проверьте имя пользователя/пароль
Время ожидания истекло. HYT00/HYT01 Медленная сеть Увеличение времени ожидания
Недопустимое имя объекта 42S02 Неправильная таблица/схема Используйте полностью квалифицированные имена
Синтаксическая ошибка 42000 Ошибка SQL Использование параметризованных запросов
Нарушение ограничений 23000 Нарушение FK/PK Проверка целостности данных
Deadlock 40001 Конкуренция за блокировку Попробуйте снова, затем проанализируйте график тупиков