pyodbc'den mssql-python'a geçiş yapın

mssql-python sürücüsü, Microsoft'un Microsoft SQL için birinci taraf Python sürücüsüdür. Microsoft tarafından desteklenen bir sürücü seçeneği tercih ederseniz, şunları sunar:

  • Harici ODBC sürücü bağımlılığı yok.
  • Yerleşik bağlantı havuzlaması.
  • Modern Python 3.10+ desteği.
  • Yerel Microsoft Entra kimlik doğrulaması.

Önemli farklar

Özellik pyodbc mssql-python
Parametre stili qmark (?) qmark (?) ve pyformat (%(name)s)
ODBC sürücüsü gerekli Evet Hayır
Bağlantı havuzlama Harici Yerleşik
Minimum Python sürümü 3.6 3.10
callproc() Destekleniyor Uygulanmadı
Varsayılan Otomatik İşleme Off Off

Temel göç adımları

Aşağıdaki adımlar, pyodbc uygulamasını mssql-python'a taşımak için yapılan temel değişiklikleri kapsar.

1. İç aktarmaları güncelle

pyodbc içe aktarmasını mssql_python ile değiştirin:

Öncesi (pyodbc):

import pyodbc

(mssql-python)'den sonra:

import mssql_python

2. Bağlantı dizilerini güncelle

Anahtar kelimeyi DRIVER= kaldırın ve kimlik doğrulama yöntemini güncelleyin:

Önce (pyodbc, ODBC sürücüsü gerektirir):

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

Sonrasında (mssql-python, sürücü gerekmiyor, Microsoft Entra kimlik doğrulaması kullanılarak):

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

3. Sorgularınızı olduğu gibi tutun

mssql-python sürücüsü hem ? (qmark) hem de %(name)s (pyformat) parametre stillerini destekler. Mevcut ? sorgularınız değişmeden çalışıyor:

Öncesi (pyodbc):

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

(mssql-python, aynı sorgu) sonra:

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

4. executemany'i olduğu gibi bırakın

Mevcut executemany çağrılar, demetler ve ? işaretleyicilerle herhangi bir değişiklik yapılmadan çalışır:

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

Sonra (mssql-python, aynı 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)

Depolanmış prosedür göçü

mssql-python sürücüsü callproc() uygulamaz. Aşağıdaki bölümler bunun yerine nasıl kullanılacağını EXECUTE gösterir.

Depolanmış prosedürler için EXECUTE kullanın

pyodbc sürücüsü callproc() öğesini destekliyor, ancak mssql-python sürücüsü desteklemiyor. Bunun yerine şunu kullanın EXECUTE :

Öncesi (pyodbc):

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

(mssql-python)'den sonra:

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

Çıkış parametreleri

Çıktı parametrelerine güvenmek yerine callproc() çıktı değerlerini yakalamak için T-SQL değişkenleri kullanın:

Önce (pyodbc, callproc kullanılarak):

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

Sonra (mssql-python, T-SQL değişkenleri kullanılarak):

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

Özelliğe özgü geçişler

Aşağıdaki bölümler, belirli pyodbc özelliklerini ve bunların mssql-python karşıtlıklarını kaplar.

Bağlantı stringleri

pyodbc anahtar kelimesi mssql-python anahtar kelimesi Notlar
DRIVER={...} Gerekli değil ODBC sürücüsü dahili olarak paketlenmiştir.
SERVER= Server= Davranış değişikliği yok.
DATABASE= Database= Davranış değişikliği yok.
Trusted_Connection= Trusted_Connection= Davranış değişikliği yok.
UID= / PWD= UID= / PWD= Davranış değişikliği yok.
Authentication= Authentication= Aynı değerleri kabul ediyor.

Otomatik Onaylama

Otomatik commit davranışı her iki sürücüde de aynıdır:

pyodbc:

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

MSSQL-Python:

conn.autocommit = True

Toplu eklemeler

Büyük INSERT toplu işlemlerini hızlandırmak için pyodbc kullanıcıları fast_executemany = True kullanır. mssql-python sürücüsü, parametreli toplu işlemler için executemany öğesini zaten optimize eder, bu nedenle orta ölçekli ekleme işlemleri için özel bir bayrak gerekmez. Büyük veri yükleri için, satırları toplu kopyalama protokolü üzerinden akıtan ve tek tek INSERT deyimleri çalıştırmaktan çok daha hızlı olan bulkcopy() tercih edin. Tam iş akışı için bkz. Toplu kopya kullan.

pyodbc:

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

(mssql-python)’dan sonra, executemany ile orta ölçekli toplu işlemler:

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)’dan sonra, bulkcopy ile büyük yükler (tercih edilir):

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

Sıra fabrikası

mssql-python sürücüsü, özel bir row factory gerektirmeden, varsayılan olarak öznitelik erişimini destekleyen Row nesneleri döndürür:

PyoDBC (özel sıra fabrikası):

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 (varsayılan olarak öznitelik erişimi):

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

Hata yönetimi

mssql-python sürücüsü, pyodbc ile aynı istisna hiyerarşisini kullanır, bu nedenle çoğu istisna işleyicisi yalnızca modül adı değişikliği gerektirir.

Özel durum hiyerarşisi

İstisna sınıf isimleri doğrudan sürücüler arasında eşleşir:

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

Hata ayrıntıları

Her iki sürücü de hata detaylarını istisna argümanları aracılığıyla ortaya çıkarır:

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

Bağlantı havuzlama

mssql-python sürücüsü varsayılan olarak bağlantı havuzlamasını içerir, bu yüzden harici havuzlama kütüphaneleri artık gerekli değildir.

Harici havuzlamayı kaldırın

Eğer pyodbc ile harici havuzlama kullandıysanız, bu özellik mssql-python sürücüsünde yerleşik olarak bulunur:

Öncesi (pyodbc dış havuz):

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

Sonra (mssql-python yerleşik havuzlama):

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

Havuzu yapılandırma

Varsayılan havuz büyüklüğü ve zaman aşımı değerlerini mssql_python.pooling() ile geçersiz kılın:

import mssql_python

mssql_python.pooling()

Tam geçiş örneği

Aşağıda aynı fonksiyonun pyodbc ile yazıldığı ve ardından mssql-python ile yeniden yazıldığı gösterilmektedir.

Öncesi (pyodbc)

Bu versiyon, pyodbc bağlantı dizesi'i bir DRIVER anahtar kelimeyle kullanır:

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

Sonra (mssql-python)

Bu sürümde anahtar DRIVER kelime kaldırılır. Tüm sorgular, parametreler ve satır erişim desenleri aynı kalır:

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

Tek değişiklikler import ifadesi ve bağlantı dizesi'dir (anahtar kelime gerekmezDRIVER). Her sorgu, parametre, getirme deseni ve satır erişimi aynı kalır.

Geçişi test etme

Göçü tamamlamadan önce, aynı sorguları her iki sürücüye karşı çalıştırın ve sonuçları karşılaştırarak eşdeğer davranışı doğrulayın.

Eşdeğer davranışı doğrulayın

Her iki sürücüye karşı aynı sorguyu çalıştıran ve sonuçların eşleşmesini belirten bir karşılaştırma fonksiyonu kullanın:

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

  • [ ] İçe aktarma işlemlerini pyodbc konumundan mssql_python konumuna güncelle.
  • [ ] Bağlantı dizilerinden çıkar DRIVER= .
  • [ ] Mevcut ? parametre sorgularını koruyun (olduğu gibi çalışırlar).
  • [ ] Saklı yordam çağrıları için EXECUTE deyimlerini kullanın.
  • [ ] Harici bağlantı havuzlama yapılandırmasını kaldırın.
  • [ ] İstisna işleme sınıf isimlerini güncelledin.
  • [ ] Tüm sorguları ve saklanan prosedürleri test edin.
  • [ ] Veri tipi işleme işlemlerini (özellikle ondalık ve tarihler) doğrulayın.
  • [ ] ODBC sürücüsünü dağıtım gereksinimlerinden kaldırın.