Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Драйвер 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 из требований к развертыванию.