Migrujte z pyodbc na mssql-python

Ovladač mssql-python je první oficiální ovladač Microsoftu pro Python pro Microsoft SQL. Pokud preferujete možnost ovladačů spravovaných Microsoft, nabízí:

  • Žádná závislost na externím ODBC ovladači.
  • Vestavěné sdružování připojení.
  • Podpora moderního Python 3.10+.
  • Nativní Microsoft Entra autentizace.

Hlavní rozdíly

funkce pyodbc mssql-python
Styl parametrů qmark (?) qmark(?) a pyformat ()%(name)s
Vyžaduje se ODBC ovladač Yes Ne
Sdílení připojení Externí Vestavěný
Minimální verze Pythonu 3.6 3.10
callproc() Podporováno Není implementováno
Výchozí automatické potvrzení Off Off

Základní migrační kroky

Následující kroky pokrývají změny klíčů pro migraci aplikace pyodbc do mssql-python.

1. Aktualizovat importy

Nahraďte import pyodbc za mssql_python:

Předtím (pyodbc):

import pyodbc

Po (mssql-python):

import mssql_python

2. Aktualizovat spojovací řetězce

Odstraňte klíčové slovo DRIVER= a aktualizujte způsob ověřování:

Před (pyodbc, vyžaduje ovladač ODBC):

conn = pyodbc.connect(
    "DRIVER={ODBC Driver 18 for SQL Server};"
    "SERVER=localhost;"
    "DATABASE=AdventureWorks2022;"
    "Trusted_Connection=yes;"
)

Po (mssql-python, bez potřeba ovladače, pomocí Microsoft Entra autentizace):

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

3. Ponechte své dotazy beze změny

Ovladač mssql-python podporuje jak ? (qmark), tak (pyformat) styly %(name)s parametrů. Vaše stávající ? dotazy fungují bez změn:

Předtím (pyodbc):

cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))

Po (mssql-python, stejný dotaz):

cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))

4. Ponechte executemany beze změny

Stávající executemany volání s nticemi a značkami ? fungují beze změny:

Předtím (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)

Po (mssql-python, stejný kód):

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)

Migrace uložených procedur

Ovladač mssql-python neimplementuje callproc(). Následující sekce ukazují, jak místo toho použít EXECUTE .

Použijte EXECUTE pro uložené procedury

Ovladač pyodbc podporuje callproc(), ale ovladač mssql-python ne. Místo toho použijte EXECUTE :

Předtím (pyodbc):

cursor.callproc("dbo.uspGetEmployeeManagers", (5,))
results = cursor.fetchall()

Po (mssql-python):

cursor.execute(
    "EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(id)s",
    {"id": 5}
)
results = cursor.fetchall()
print(f"Got {len(results)} rows")

Výstupní parametry

Použijte T-SQL proměnné k zachycení výstupních hodnot místo spoléhání se na callproc() výstupní parametry:

Předtím (pyodbc, při použití callproc):

params = (category_id, pyodbc.SQL_INTEGER)
cursor.callproc("dbo.GetProductCount", params)
count = params[1].value

Po (mssql-python, s použitím proměnných 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}")

Migrace pro konkrétní funkce

Následující sekce pokrývají konkrétní funkce pyodbc a jejich ekvivalenty mssql-python.

Připojovací řetězce

Klíčové slovo pyodbc Klíčové slovo mssql-python Poznámky
DRIVER={...} Není potřeba Ovladač ODBC je součástí balení.
SERVER= Server= Žádné změny chování.
DATABASE= Database= Žádné změny chování.
Trusted_Connection= Trusted_Connection= Žádné změny chování.
UID= / PWD= UID= / PWD= Žádné změny chování.
Authentication= Authentication= Přijímá stejné hodnoty.

Autocommit

V obou ovladačích je chování automatického potvrzování identické:

pyodbc:

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

mssql-python:

conn.autocommit = True

Hromadné vkládání

Pro urychlení velkých INSERT dávek uživatelé pyodbc nastavují fast_executemany = True. Ovladač mssql-python už optimalizuje executemany pro parametrizované dávky, takže středně velké objemy vkládání nevyžadují žádný speciální příznak. Pro velké datové zatížení preferujeme bulkcopy(), které streamuje řádky přes protokol hromadné kopie a je mnohem rychlejší než vydávání jednotlivých INSERT příkazů. Pro celý pracovní postup viz Použít hromadnou kopii.

pyodbc:

cursor.fast_executemany = True
cursor.executemany(query, data)

Po (mssql-python) moderujte dávky s 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()

Po použití (mssql-python), rozsáhlá načítání s bulkcopy (doporučeno):

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

Generátor řádků

Ovladač mssql-python vrací objekty Row, které ve výchozím nastavení podporují přístup k atributům, aniž by byla vyžadována vlastní row factory:

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 (přístup k atributům ve výchozím nastavení):

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

Zpracování chyb

Ovladač mssql-python používá stejnou hierarchii výjimek jako pyodbc, takže většina obslužných nástrojů výjimek vyžaduje pouze změnu názvu modulu.

Hierarchie výjimek

Názvy tříd výjimek se mapují přímo mezi ovladači:

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

Podrobnosti o chybě

Oba ovladače odhalují chybové detaily prostřednictvím výjimek:

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

Sdílení připojení

Ovladač mssql-python ve výchozím nastavení zahrnuje sdružování připojení, takže externí knihovny pro sdružování připojení už nejsou potřeba.

Odeberte externí sdružování

Pokud jste použili externí pooling s pyodbc, ovladač mssql-python to má zabudované:

Před (externí bazén 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()

Po (integrovaném sdružování připojení v mssql-python):

conn = mssql_python.connect(connection_string)
conn.close()

Konfigurovat fond

Přepište výchozí velikost poolu a časový limit pomocí mssql_python.pooling():

import mssql_python

mssql_python.pooling()

Příklad dokončení migrace

Následující ukazuje stejnou funkci napsanou v pyodbc a poté přepsánou v mssql-python.

Předtím (pyodbc)

Tato verze používá pyodbc připojovací řetězec s klíčovým slovemDRIVER:

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

Po (mssql-python)

Tato verze odstraní klíčové slovo DRIVER . Všechny dotazy, parametry a vzory přístupu k řádkům zůstávají totožné:

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

Jediné změny jsou příkaz import a připojovací řetězec (klíčové slovo není DRIVER potřeba). Každý dotaz, parametr, vzor načtení a přístup k řádku zůstávají stejné.

Otestujte migraci

Před dokončením migrace spusťte stejné dotazy v obou ovladačích a porovnejte výsledky, abyste ověřili ekvivalentní chování.

Ověřte ekvivalentní chování

Použijte srovnávací funkci, která spustí stejný dotaz na oběma ovladačích a potvrdí, že výsledky se shodují:

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

Kontrolní seznam

  • [ ] Aktualizovat importy z pyodbc do .mssql_python
  • [ ] Odstraňte DRIVER= z propojovacích řetězců.
  • [ ] Udržujte existující ? dotazy na parametry (fungují as-is).
  • [ ] Použijte EXECUTE příkazy pro volání uložených procedur.
  • [ ] Odstraňte konfiguraci externího připojení pro pooling.
  • [ ] Aktualizace názvů tříd při zpracování výjimek.
  • [ ] Otestujte všechny dotazy a uložené procedury.
  • [ ] Ověřte zpracování datových typů (zejména desetinné čísla a data).
  • [ ] Odstraňte ovladač ODBC z požadavků na nasazení.