Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Драйвер mssql-python — это оригинальный Python-драйвер Microsoft для Microsoft SQL. Если вы предпочитаете опцию драйвера, поддерживаемого Microsoft, он предлагает:
- Нет внешних зависимостей от драйвера ODBC.
- Встроенный пул соединений.
- Поддержка современного Python 3.10+.
- Нативная аутентификация Microsoft Entra.
Основные отличия
| Функция | pyodbc | mssql-python |
|---|---|---|
| Стиль параметров |
qmark (?) |
qmark (?) и pyformat (%(name)s) |
| Требуется драйвер ODBC | Yes | нет |
| Пулинг соединений | External | Built-in |
| Минимальная версия Python | 3.6 | 3.10 |
callproc() |
Поддерживается | Не реализовано |
| Автоматическое подтверждение по умолчанию | Off | Off |
Основные шаги миграции
Следующие шаги охватывают ключевые изменения для миграции приложения pyodbc на mssql-python.
1. Обновление импорта
Замените pyodbc импорт на mssql_python:
До (pyodbc):
import pyodbc
После (mssql-python):
import mssql_python
2. Обновить строки соединения
Удалите ключевое DRIVER= слово и обновите метод аутентификации:
Раньше (pyodbc, требуется драйвер ODBC):
conn = pyodbc.connect(
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=localhost;"
"DATABASE=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
После (mssql-python, драйвер не требуется, с использованием аутентификации Microsoft Entra):
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
3. Оставляйте свои запросы без изменений
Драйвер mssql-python поддерживает оба стиля параметров: ? (qmark) и %(name)s (pyformat). Ваши существующие ? запросы работают без изменений:
До (pyodbc):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
После (mssql-python, тот же запрос):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
4. Оставьте executemany как есть
Существующие executemany вызовы с кортежами и ? маркерами работают без изменений:
До (pyodbc):
cursor.execute("CREATE TABLE #MigrateDemo (ID INT, Name NVARCHAR(50))")
data = [(1, "Alice"), (2, "Bob"), (3, "Carol")]
cursor.executemany("INSERT INTO #MigrateDemo (ID, Name) VALUES (?, ?)", data)
После (mssql-python, тот же код):
cursor.execute("DROP TABLE IF EXISTS #MigrateDemo")
cursor.execute("CREATE TABLE #MigrateDemo (ID INT, Name NVARCHAR(50))")
data = [(1, "Alice"), (2, "Bob"), (3, "Carol")]
cursor.executemany("INSERT INTO #MigrateDemo (ID, Name) VALUES (?, ?)", data)
Миграция хранящихся процедур
Драйвер mssql-python не реализует callproc(). В следующих разделах показано, как использовать EXECUTE вместо него.
Используйте EXECUTE для хранящихся процедур
Драйвер pyodbc поддерживает callproc(), а драйвер mssql-python — нет. Используйте EXECUTE вместо этого:
До (pyodbc):
cursor.callproc("dbo.uspGetEmployeeManagers", (5,))
results = cursor.fetchall()
После (mssql-python):
cursor.execute(
"EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(id)s",
{"id": 5}
)
results = cursor.fetchall()
print(f"Got {len(results)} rows")
Выходные параметры
Используйте переменные T-SQL для получения выходных значений вместо использования параметров вывода callproc():
До (pyodbc, с использованием callproc):
params = (category_id, pyodbc.SQL_INTEGER)
cursor.callproc("dbo.GetProductCount", params)
count = params[1].value
После (mssql-python, с использованием переменных T-SQL):
cursor.execute(
"""
DECLARE @count INT;
SELECT @count = COUNT(*) FROM Production.Product
WHERE ProductSubcategoryID = %(cat_id)s;
SELECT @count AS ProductCount;
""",
{"cat_id": 1}
)
product_count = cursor.fetchval()
print(f"Product count: {product_count}")
Миграции для конкретных функций
Следующие разделы охватывают конкретные функции pyodbc и их аналоги на mssql-python.
Строки подключения
| Ключевое слово pyodbc | Ключевое слово mssql-python | Примечания. |
|---|---|---|
DRIVER={...} |
Не требуется | Драйвер ODBC встроен. |
SERVER= |
Server= |
Нет изменений в поведении. |
DATABASE= |
Database= |
Нет изменений в поведении. |
Trusted_Connection= |
Trusted_Connection= |
Нет изменений в поведении. |
UID= / PWD= |
UID= / PWD= |
Нет изменений в поведении. |
Authentication= |
Authentication= |
Принимает те же ценности. |
Автокоммит
Поведение автокоммита идентично в обоих драйверах:
pyodbc:
conn.autocommit = True
pyodbc.connect(connection_string, autocommit=True)
mssql-python:
conn.autocommit = True
Пакетные вставки
Чтобы ускорить большие INSERT партии, пользователи pyodbc устанавливают fast_executemany = True. Драйвер mssql-python уже оптимизирует executemany для параметризованных пакетов, поэтому умеренные вставки не требуют специального флага. При больших объёмах загрузки данных используйте bulkcopy(), который потоково передаёт строки по протоколу массового копирования и работает гораздо быстрее, чем выполнение отдельных операторов INSERT. Описание полного процесса см. в разделе «Использование массового копирования».
pyodbc:
cursor.fast_executemany = True
cursor.executemany(query, data)
После (mssql-python), средние пакеты с executemany:
cursor.execute("DROP TABLE IF EXISTS #BulkTarget")
cursor.execute("CREATE TABLE #BulkTarget (ID INT, Name NVARCHAR(50))")
data = [(i, f"Item {i}") for i in range(100)]
cursor.executemany("INSERT INTO #BulkTarget (ID, Name) VALUES (?, ?)", data)
conn.commit()
После (mssql-python) большие загрузки с bulkcopy (предпочтительно):
cursor.execute("IF OBJECT_ID('##BulkTarget') IS NOT NULL DROP TABLE ##BulkTarget")
cursor.execute("CREATE TABLE ##BulkTarget (ID INT, Name NVARCHAR(50))")
conn.commit() # Commit DDL before bulkcopy
data = [(i, f"Item {i}") for i in range(100)]
result = cursor.bulkcopy("##BulkTarget", data)
print(f"Bulk copied {result['rows_copied']} rows")
cursor.execute("DROP TABLE ##BulkTarget")
conn.commit()
Фабрика строк
Драйвер mssql-python возвращает объекты Row, поддерживающие доступ к атрибутам по умолчанию, без необходимости использовать пользовательскую фабрику строк:
PYODBC (Custom Row Factory):
def namedtuple_row_factory(cursor):
from collections import namedtuple
columns = [col[0] for col in cursor.description]
Row = namedtuple("Row", columns)
return Row
mssql-python (по умолчанию доступ к атрибутам):
cursor.execute("SELECT Name, ListPrice FROM Production.Product")
row = cursor.fetchone()
print(row.Name) # Attribute access works directly
print(row[0]) # Index access also works
Обработка ошибок
Драйвер mssql-python использует ту же иерархию исключений, что и pyodbc, поэтому большинство обработчиков исключений требуют только изменения имени модуля.
Иерархия исключений
Имена классов исключений напрямую соответствуют друг другу в разных драйверах:
pyodbc:
try:
cursor.execute(query)
except pyodbc.Error as e:
pass
except pyodbc.DatabaseError as e:
pass
except pyodbc.OperationalError as e:
pass
mssql-python:
try:
cursor.execute("SELECT TOP 1 * FROM Production.Product")
print(cursor.fetchone())
except mssql_python.Error as e:
pass
except mssql_python.DatabaseError as e:
pass
except mssql_python.OperationalError as e:
pass
Сведения об ошибке
Оба драйвера раскрывают детали ошибок через аргументы исключений:
pyodbc:
try:
cursor.execute(query)
except pyodbc.Error as e:
sqlstate = e.args[0]
message = e.args[1]
mssql-python:
try:
cursor.execute("SELECT TOP 1 * FROM NonExistentTable_XYZ")
except mssql_python.Error as e:
# Error message contains SQLSTATE and details
print(str(e))
Пулинг соединений
Драйвер mssql-python по умолчанию включает пул соединений, поэтому внешние библиотеки пула больше не требуются.
Удалить внешнее объединение ресурсов
Если вы использовали внешний пул подключений с pyodbc, в драйвере mssql-python эта возможность уже встроена:
До (внешнего пула pyodbc):
from dbutils.pooled_db import PooledDB
pool = PooledDB(pyodbc, 5, driver="{ODBC Driver 18 for SQL Server}",
server="your_server", database="your_database",
uid="your_username", pwd="your_password")
conn = pool.connection()
После (встроенное пулирование mssql-python):
conn = mssql_python.connect(connection_string)
conn.close()
Конфигурация пула
Переопределите стандартный размер пула и тайм-аут с помощью mssql_python.pooling():
import mssql_python
mssql_python.pooling()
Пример завершения миграции
Ниже показана та же функция, записанная с помощью pyodbc и затем перезаписанная с помощью mssql-python.
До (pyodbc)
В этой версии используется строка подключения pyodbc с ключевым словом DRIVER:
import pyodbc
from datetime import date
def get_orders(customer_id: int, start_date: date):
conn = pyodbc.connect(
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=localhost;"
"DATABASE=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
cursor = conn.cursor()
cursor.execute("""
SELECT SalesOrderID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE CustomerID = ? AND OrderDate >= ?
ORDER BY OrderDate DESC
""", (customer_id, start_date))
orders = []
for row in cursor:
orders.append({
"id": row.SalesOrderID,
"date": row.OrderDate,
"total": row.TotalDue
})
cursor.close()
conn.close()
return orders
После (mssql-python)
В этой версии удалено ключевое DRIVER слово. Все запросы, параметры и шаблоны доступа к строкам остаются идентичными:
import mssql_python
from datetime import date
def get_orders(customer_id: int, start_date: date):
conn = mssql_python.connect(
"Server=localhost;"
"Database=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
cursor = conn.cursor()
cursor.execute("""
SELECT SalesOrderID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE CustomerID = ? AND OrderDate >= ?
ORDER BY OrderDate DESC
""", (customer_id, start_date))
orders = []
for row in cursor:
orders.append({
"id": row.SalesOrderID,
"date": row.OrderDate,
"total": row.TotalDue
})
cursor.close()
conn.close()
return orders
Единственные изменения — оператор import и строка подключения (ключевое слово DRIVER не требуется). Каждый запрос, параметр, шаблон выборки и доступ к строкам остаются идентичными.
Тестирование миграции
Перед завершением миграции выполните те же запросы к обоим драйверам и сравните результаты, чтобы подтвердить эквивалентное поведение.
Проверьте эквивалентное поведение
Используйте функцию сравнения, которая выполняет один и тот же запрос к обоим драйверам и подтверждает совпадение результатов:
import pyodbc
import mssql_python
def compare_results(pyodbc_conn_str: str, mssql_conn_str: str, query: str):
"""Compare results from both drivers."""
# pyodbc query
pyodbc_conn = pyodbc.connect(pyodbc_conn_str)
pyodbc_cursor = pyodbc_conn.cursor()
pyodbc_cursor.execute(query)
pyodbc_results = pyodbc_cursor.fetchall()
pyodbc_conn.close()
# mssql-python query
mssql_conn = mssql_python.connect(mssql_conn_str)
mssql_cursor = mssql_conn.cursor()
mssql_cursor.execute(query)
mssql_results = mssql_cursor.fetchall()
mssql_conn.close()
# Compare
assert len(pyodbc_results) == len(mssql_results)
for p_row, m_row in zip(pyodbc_results, mssql_results):
assert tuple(p_row) == tuple(m_row)
print(f"Results match: {len(pyodbc_results)} rows")
Checklist
- [ ] Обновить импорт из
pyodbcвmssql_python. - [ ] Снимите
DRIVER=с соединительных строк. - [ ] Сохраняйте существующие
?запросы параметров (они работают as-is). - [ ] Используйте инструкции
EXECUTEдля вызовов хранимых процедур. - [ ] Удалите конфигурацию внешнего пулирования соединений.
- [ ] Обновить имена классов для обработки исключений.
- [ ] Проверьте все запросы и хранящиеся процедуры.
- [ ] Проверьте обработку типов данных (особенно десятичных и дат).
- [ ] Удалить драйвер ODBC из требований к развертыванию.