Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le pilote mssql-python est le pilote Python de première partie de Microsoft pour Microsoft SQL. Si vous préférez une option de pilote maintenue par Microsoft, elle propose :
- Aucune dépendance externe à un pilote ODBC.
- Pool de connexions intégré.
- Prise en charge moderne de Python 3.10 et versions ultérieures.
- Authentification Microsoft Entra native.
Principales différences
| Fonctionnalité | pyodbc | mssql-python |
|---|---|---|
| Style de paramètre |
qmark (?) |
qmark (?) et pyformat (%(name)s) |
| Pilote ODBC requis | Oui | Non |
| Regroupement de connexions | Externe | Intégré |
| Minimum Python | 3.6 | 3.10 |
callproc() |
Soutenu | Non implémenté |
| Validation automatique par défaut | Off | Off |
Étapes de base de la migration
Les étapes suivantes couvrent les changements clés pour migrer une application pyodbc vers mssql-python.
1. Mise à jour des importations
Remplacez l’importation pyodbc par mssql_python:
Avant (pyodbc) :
import pyodbc
Après (mssql-python) :
import mssql_python
2. Mettre à jour les chaînes de connexion
Supprimez le DRIVER= mot-clé et mettez à jour la méthode d’authentification :
Avant (pyodbc, nécessite un pilote ODBC) :
conn = pyodbc.connect(
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=localhost;"
"DATABASE=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
Après (mssql-python, aucun pilote requis, avec l’authentification Microsoft Entra) :
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
3. Conservez vos requêtes telles quelles
Le pilote mssql-python prend en charge à la fois ? (qmark) et %(name)s (pyformat) comme styles de paramètres. Vos requêtes existantes ? fonctionnent sans modifications :
Avant (pyodbc) :
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
Après (mssql-python, même requête) :
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
4. Conservez executemany tel quel
Les appels existants utilisant des tuples executemany et des marqueurs ? fonctionnent sans aucune modification :
Avant (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)
Après (mssql-python, même code) :
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)
Migration des procédures stockées
Le pilote mssql-python n’implémente pas callproc(). Les sections suivantes montrent comment utiliser EXECUTE à la place.
Utilisez EXECUTE pour les procédures stockées
Le pilote pyodbc prend en charge callproc(), mais le pilote mssql-python ne le fait pas. Utilisez EXECUTE à la place :
Avant (pyodbc) :
cursor.callproc("dbo.uspGetEmployeeManagers", (5,))
results = cursor.fetchall()
Après (mssql-python) :
cursor.execute(
"EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(id)s",
{"id": 5}
)
results = cursor.fetchall()
print(f"Got {len(results)} rows")
Paramètres de sortie
Utilisez des variables T-SQL pour capturer les valeurs de sortie au lieu de vous fier aux callproc() paramètres de sortie :
Avant (pyodbc, en utilisant le callproc) :
params = (category_id, pyodbc.SQL_INTEGER)
cursor.callproc("dbo.GetProductCount", params)
count = params[1].value
Après (mssql-python, utilisant des variables 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}")
Migrations spécifiques à chaque fonctionnalité
Les sections suivantes couvrent des caractéristiques spécifiques de pyodbc et leurs équivalents mssql-python.
Chaînes de connexion
| Mot-clé pyodbc | mot-clé mssql-python | Notes |
|---|---|---|
DRIVER={...} |
Non nécessaire | Le pilote ODBC est intégré en interne. |
SERVER= |
Server= |
Aucun changement de comportement. |
DATABASE= |
Database= |
Aucun changement de comportement. |
Trusted_Connection= |
Trusted_Connection= |
Aucun changement de comportement. |
UID= / PWD= |
UID= / PWD= |
Aucun changement de comportement. |
Authentication= |
Authentication= |
Accepte les mêmes valeurs. |
Validation automatique
Le comportement d’autocommit est identique dans les deux pilotes :
pyodbc :
conn.autocommit = True
pyodbc.connect(connection_string, autocommit=True)
MSSQL-python :
conn.autocommit = True
Insertions en masse
Pour accélérer les lots volumineux de INSERT, les utilisateurs de pyodbc définissent fast_executemany = True. Le pilote mssql-python optimise executemany déjà pour les lots paramétrés, donc les insertions modérées n’ont pas besoin de drapeau spécial. Pour les charges de données importantes, préférez bulkcopy(), qui diffuse les lignes via le protocole de copiage en masse et est beaucoup plus rapide que l’émission d’instructions individuelles INSERT . Pour le flux de travail complet, voir Utiliser la copie en masse.
pyodbc :
cursor.fast_executemany = True
cursor.executemany(query, data)
Après (mssql-python), lots de taille modérée avec 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()
Après (mssql-python), charges importantes avec bulkcopy (préférable) :
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()
Fabrique de ligne
Le pilote mssql-python renvoie par défaut des objets Row permettant l’accès aux attributs, sans nécessiter de fabrique de lignes personnalisée :
pyodbc (fabrique de lignes personnalisée) :
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 (accès par défaut aux attributs) :
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
Gestion des erreurs
Le pilote mssql-python utilise la même hiérarchie d’exception que pyodbc, donc la plupart des gestionnaires d’exceptions ne nécessitent qu’un changement de nom de module.
Hiérarchie d’exceptions
Les noms des classes d’exception correspondent directement entre les pilotes :
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
Détails de l’erreur
Les deux conducteurs exposent les détails des erreurs à travers des arguments d’exception :
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))
Regroupement de connexions
Le pilote mssql-python inclut par défaut le pooling de connexions, donc les bibliothèques de pooling externes ne sont plus nécessaires.
Supprimer la mise en commun externe
Si vous avez utilisé un pooling externe avec pyodbc, le pilote mssql-python l’a intégré :
Avant (pool externe 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()
Après (pooling intégré mssql-python) :
conn = mssql_python.connect(connection_string)
conn.close()
Configurer le pool
Supplantez la taille du pool par défaut et le délai d’expiration par :mssql_python.pooling()
import mssql_python
mssql_python.pooling()
Exemple de migration complet
Ce qui suit montre la même fonction écrite avec pyodbc puis réécrite avec mssql-python.
Avant (pyodbc)
Cette version utilise la chaîne de connexion pyodbc avec le mot-clé 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
Après (mssql-python)
Cette version supprime le DRIVER mot-clé. Toutes les requêtes, paramètres et schémas d’accès aux lignes restent identiques :
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
Les seuls changements concernent l’instruction d’importation et la chaîne de connexion (aucun mot-clé DRIVER n’est nécessaire). Chaque requête, chaque paramètre, chaque schéma d’extraction et chaque accès aux lignes restent identiques.
Test de migration
Avant de terminer la migration, effectuez les mêmes requêtes sur les deux pilotes et comparez les résultats pour confirmer un comportement équivalent.
Vérifier le comportement équivalent
Utilisez une fonction de comparaison qui exécute la même requête sur les deux pilotes et affirme que les résultats correspondent :
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")
Liste de contrôle
- [ ] Mettez à jour les imports de
pyodbcversmssql_python. - [ ] Supprimez
DRIVER=des chaînes de connexion. - [ ] Conservez les requêtes de paramètres existantes
?(elles fonctionnent as-is). - [ ] Utilisez les instructions
EXECUTEpour les appels à des procédures stockées. - [ ] Supprimer la configuration du groupement de connexions externe.
- [ ] Mettre à jour les noms des classes de gestion des exceptions.
- [ ] Testez toutes les requêtes et procédures stockées.
- [ ] Vérifier la gestion des types de données (en particulier les décimales et les dates).
- [ ] Retirez le pilote ODBC des exigences de déploiement.