Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
MSSQL-python-drivrutinen är Microsoft förstaparts Python-drivrutin för Microsoft SQL. Om du föredrar ett drivrutinsalternativ som underhålls av Microsoft erbjuds det:
- Inget beroende av extern ODBC-drivrutin.
- Inbyggd anslutningspooling.
- Stöd för modern Python 3.10+
- Inbyggd Microsoft Entra-autentisering.
Viktiga skillnader
| Feature | pyodbc | mssql-python |
|---|---|---|
| Parameterstil |
qmark (?) |
qmark (?) och pyformat (%(name)s) |
| ODBC-förare krävs | Ja | No |
| Anslutningspoolning | Externt | Inbyggd |
| Minsta Python-version | 3.6 | 3.10 |
callproc() |
Stöds | Inte implementerad |
| Standard för automatisk incheckning | Off | Off |
Grundläggande migrationssteg
Följande steg täcker nyckeländringarna för att migrera en pyodbc-applikation till mssql-python.
1. Uppdatera importen
Ersätt importen pyodbc med mssql_python:
Före (pyodbc):
import pyodbc
Efter (mssql-python):
import mssql_python
2. Uppdatera anslutningssträngar
Ta bort DRIVER= nyckelordet och uppdatera autentiseringsmetoden:
Innan (pyodbc, kräver ODBC-drivrutin):
conn = pyodbc.connect(
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=localhost;"
"DATABASE=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
Efter (mssql-python, ingen drivrutin behövs, använder Microsoft Entra-autentisering):
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
3. Behåll dina frågor som de är
mssql-python-drivrutinen stöder både ? (qmark) och %(name)s (pyformat) parameterstilar. Dina befintliga ? frågor fungerar utan ändringar:
Före (pyodbc):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
Efter (mssql-python, samma fråga):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
4. Behåll executemany som det är
Befintliga executemany anrop med tupler och ? markörer fungerar utan ändringar:
Före (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)
Efter (mssql-python, samma kod):
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)
Migrering av lagrad procedur
mssql-python-drivrutinen implementerar inte callproc(). Följande avsnitt visar hur man i stället använder EXECUTE.
Använd EXECUTE för lagrade procedurer
Pyodbc-drivrutinen stödjer callproc(), men mssql-python-drivrutinen gör det inte. Använd EXECUTE i stället:
Före (pyodbc):
cursor.callproc("dbo.uspGetEmployeeManagers", (5,))
results = cursor.fetchall()
Efter (mssql-python):
cursor.execute(
"EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(id)s",
{"id": 5}
)
results = cursor.fetchall()
print(f"Got {len(results)} rows")
Utdataparametrar
Använd T-SQL-variabler för att fånga utdatavärden istället för att förlita dig på callproc() utdataparametrar:
Innan (pyodbc, använder callproc):
params = (category_id, pyodbc.SQL_INTEGER)
cursor.callproc("dbo.GetProductCount", params)
count = params[1].value
Efter (mssql-python, med T-SQL-variabler):
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}")
Migreringar för specifika funktioner
Följande avsnitt täcker specifika pyodbc-funktioner och deras motsvarigheter till mssql-python.
Anslutningssträngar
| pyodbc-nyckelord | mssql-python-nyckelordet | Noteringar |
|---|---|---|
DRIVER={...} |
Behövs inte | ODBC-drivrutinen är paketerad internt. |
SERVER= |
Server= |
Inget beteende ändras. |
DATABASE= |
Database= |
Inget beteende ändras. |
Trusted_Connection= |
Trusted_Connection= |
Inget beteende ändras. |
UID= / PWD= |
UID= / PWD= |
Inget beteende ändras. |
Authentication= |
Authentication= |
Accepterar samma värden. |
Autocommit
Beteendet för autocommit är identiskt i båda drivrutinerna:
Pyodbc:
conn.autocommit = True
pyodbc.connect(connection_string, autocommit=True)
mssql-python:
conn.autocommit = True
Bulkinsatser
För att snabba upp stora INSERT batcher sätter pyodbc-användare in fast_executemany = True. mssql-python-drivrutinen optimerar executemany redan för parameteriserade batcher, så måttliga inserts behöver ingen särskild flagga. För stora datalaster, föredra bulkcopy(), vilket strömmar rader över bulkkopieringsprotokollet och är mycket snabbare än att utfärda enskilda INSERT uttalanden. För hela arbetsflödet, se Använd bulkkopiering.
Pyodbc:
cursor.fast_executemany = True
cursor.executemany(query, data)
Efter (mssql-python), moderera batchar med 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()
Efter (mssql-python), stora laster med bulkcopy (föredras):
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()
Radfabrik
mssql-python-drivrutinen returnerar Row objekt som stöder attributåtkomst som standard, utan att kräva en anpassad radfabrik:
pyodbc (anpassad radgenerator):
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 (attributåtkomst som standard):
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
Felhantering
mssql-python-drivrutinen använder samma undantagshierarki som pyodbc, så de flesta undantagshanterare kräver endast ett modulnamnbyte.
Undantagshierarki
Undantagsklassnamnen mappas direkt mellan drivrutinerna:
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
Felinformation
Båda drivrutinerna exponerar feldetaljer genom undantagsargument:
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))
Anslutningspoolning
mssql-python-drivrutinen inkluderar anslutningspooling som standard, så externa poolingbibliotek behövs inte längre.
Ta bort extern sammanslagning
Om du använde extern pooling med pyodbc har mssql-python-drivrutinen det inbyggt:
Före (pyodbc extern pool):
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()
Efter (mssql-python inbyggd poolning):
conn = mssql_python.connect(connection_string)
conn.close()
Konfigurera pool
Överstyr standardpoolstorleken och timeout med mssql_python.pooling():
import mssql_python
mssql_python.pooling()
Exempel på fullständig migrering
Följande visar samma funktion skriven med pyodbc och sedan omskriven med mssql-python.
Före (pyodbc)
Denna version använder pyodbc-reťazec pripojenia med ett DRIVER nyckelord:
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
Efter (mssql-python)
Denna version tar bort DRIVER nyckelordet. Alla frågor, parametrar och radåtkomstmönster förblir identiska:
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
De enda ändringarna är importsatsen och reťazec pripojenia (inget DRIVER nyckelord behövs). Varje fråga, parameter, hämtamönster och radåtkomst förblir identisk.
Testa migrering
Innan du slutför migreringen, kör samma frågor mot båda drivrutinerna och jämför resultaten för att bekräfta motsvarande beteende.
Verifiera ekvivalent beteende
Använd en jämförelsefunktion som kör samma fråga mot båda drivrutinerna och bekräftar resultatmatchningen:
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")
Checklista
- [ ] Uppdatera importer från
pyodbctillmssql_python. - [ ] Ta bort
DRIVER=från anslutningssträngar. - [ ] Behåll befintliga
?parameterfrågor (de fungerar as-is). - [ ] Använd
EXECUTEinstruktioner vid anrop av lagrade procedurer. - [ ] Ta bort konfigurationen för extern anslutningspooling.
- [ ] Uppdatera klassnamn för hantering av undantag.
- [ ] Testa alla frågor och lagrade procedurer.
- [ ] Verifiera hantering av datatyper (särskilt decimaler och datum).
- [ ] Ta bort ODBC-drivrutinen från distributionskraven.