Migreren van pymssql naar mssql-python

De mssql-python-driver is Microsoft's eigen Python-driver voor Microsoft SQL. Als je liever een door Microsoft onderhouden driveroptie hebt, biedt het:

  • Geen FreeTDS-afhankelijkheid.
  • Meerdere gelijktijdige cursors per verbinding.
  • Ingebouwde verbindingspooling.
  • Ondersteuning voor moderne Python 3.10+-versies.
  • Ingebouwde Microsoft Entra-authenticatie.
  • Rijobjecten met standaard attribuuttoegang.

Belangrijkste verschillen

Feature pymssql mssql-python
Parameterstijl format (%s, %d) qmark (?) en pyformat (%(name)s)
Inheemse bibliotheek FreeTDS DDBC (gebundeld)
Groepsgewijze verbindingen External Built-in
Cursors per verbinding 1 Multiple
Minimale Python-versie 3.6 3.10
callproc() Supported Niet geïmplementeerd
as_dict cursor Extension Rijobjecten (standaard)
Bulksgewijs kopiëren conn.bulk_copy() cursor.bulkcopy()
Autocommit standaardinstelling Off Off

Basisstappen voor migratie

De volgende stappen bespreken de meest voorkomende wijzigingen die nodig zijn om een pymssql-applicatie naar mssql-python te migreren.

1. Werk importinstructies bij

Vervang de pymssql import door mssql_python:

Voor (pymssql):

import pymssql

Na (mssql-python):

import mssql_python

2. Verbindingsaanroepen bijwerken

PymsSQL gebruikt positionele argumenten. De mssql-python-driver gebruikt een verbindingsreeks of trefwoordargumenten:

Voor (pymssql, positionele argumenten):

conn = pymssql.connect("<server>", "<user>", "<password>", "<database>")

Voor (pymssql, trefwoordargumenten):

conn = pymssql.connect(
    host=r"<server>\<instance>",
    user="<login>",
    password="<password>",
    database="<database>"
)

Na (mssql-python, verbindingsreeks):

conn = mssql_python.connect(
    "Server=<server>;"
    "Database=<database>;"
    "UID=<username>;"
    "PWD=<password>;"
    "Encrypt=yes;"
)

Na (mssql-python, Microsoft Entra raadde aan):

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

3. Parametermarkers bijwerken

PymsSQL gebruikt %s en %d formateert plaatsvervangers. De mssql-python-driver gebruikt ? (qmark) of %(name)s (pyformat):

Voor (pymssql):

cursor.execute("SELECT * FROM Person.Person WHERE BusinessEntityID = %d AND FirstName = %s", (user_id, name))

Na (mssql-python, qmark-stijl):

user_id, name = 1, "Ken"
cursor.execute("SELECT * FROM Person.Person WHERE BusinessEntityID = ? AND FirstName = ?", (user_id, name))
print(cursor.fetchone())

cursor.execute(
    "SELECT * FROM Person.Person WHERE BusinessEntityID = %(id)s AND FirstName = %(name)s",
    {"id": user_id, "name": name}
)
print(cursor.fetchone())

4. executemany bijwerken

Werk de SQL-placeholders bij van %s/%d naar ? of %(name)s:

Voor (pymssql):

cursor.execute("CREATE TABLE #Persons (ID INT, Name NVARCHAR(50), Department NVARCHAR(50))")
cursor.executemany(
    "INSERT INTO #Persons VALUES (%d, %s, %s)",
    [(1, "John", "Sales"), (2, "Jane", "Marketing")]
)

Na (mssql-python, qmark-stijl):

cursor.execute("IF OBJECT_ID('#Persons') IS NOT NULL DROP TABLE #Persons")
cursor.execute("CREATE TABLE #Persons (ID INT, Name NVARCHAR(50), Department NVARCHAR(50))")
cursor.executemany(
    "INSERT INTO #Persons VALUES (?, ?, ?)",
    [(1, "John", "Sales"), (2, "Jane", "Marketing")]
)
cursor.execute("SELECT * FROM #Persons")
for row in cursor:
    print(row)

5. Gebruik rijattributen in plaats van as_dict cursors

PymsSQL vereist as_dict=True dat kolommen op naam worden geraadpleegd. De mssql-python-driver levert objecten terug Row die standaard zowel attribuut- als indextoegang ondersteunen:

Voor (pymssql):

cursor = conn.cursor(as_dict=True)
cursor.execute("SELECT BusinessEntityID, FirstName FROM Person.Person WHERE FirstName = %s", ("John",))
for row in cursor:
    print("ID=%d, Name=%s" % (row["BusinessEntityID"], row["FirstName"]))

Na (mssql-python, standaard toegang via attributen):

cursor = conn.cursor()
cursor.execute("SELECT BusinessEntityID, FirstName FROM Person.Person WHERE FirstName = ?", ("John",))
for row in cursor:
    print(f"ID={row.BusinessEntityID}, Name={row.FirstName}")
    # Index access also works: row[0], row[1]

Migratie van opgeslagen procedures

De mssql-python-driver implementeert callproc()niet . Gebruik EXECUTE in plaats daarvan statements.

Gebruik EXECUTE voor opgeslagen procedures

PymsSQL ondersteunt callproc(), maar de MSSQL-Python-driver niet. Gebruik EXECUTE in plaats daarvan:

Voor (pymssql):

cursor.callproc("uspGetEmployeeManagers", (5,))
for row in cursor:
    print(row)

Na (mssql-python):

cursor.execute("EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = ?", (5,))
for row in cursor:
    print(row)

Uitvoerparameters

Gebruik T-SQL-variabelen om outputwaarden vast te leggen in plaats van te vertrouwen op callproc() outputparameters:

Voor (pymssql):

cursor.callproc("GetProductCount", (category_id,))
count = cursor.fetchval()

Na (mssql-python, T-SQL variabelen):

cursor.execute("""
    DECLARE @count INT;
    SELECT @count = COUNT(*) FROM Production.Product
    WHERE ProductSubcategoryID = ?;
    SELECT @count AS ProductCount;
""", (1,))
product_count = cursor.fetchval()
print(f"Product count: {product_count}")

Migratie door bulkkopiëren

pymssql roept bulk_copy() aan op de verbinding. De mssql-python-driver roept bulkcopy() op de cursor aan met extra opties:

Voor (pymssql):

conn.bulk_copy("##BulkDemo", [(1, 2)] * 1000)
conn.commit()

Na (mssql-python):

cursor = conn.cursor()
cursor.execute("CREATE TABLE ##BulkDemo (Col1 INT, Col2 INT)")
conn.commit()
result = cursor.bulkcopy("##BulkDemo", [(1, 2)] * 1000)
print(f"Copied {result['rows_copied']} rows")
conn.commit()
cursor.execute("DROP TABLE ##BulkDemo")
conn.commit()

De mssql-python-methode bulkcopy() ondersteunt batch_size, timeout, column_mappings, keep_identity, check_constraintstable_lockkeep_nullsfire_triggers, en .use_internal_transaction Zie Bulk copy voor details.

Meerdere cursors

PymsSQL staat slechts één actieve cursor per verbinding toe. De mssql-python-driver ondersteunt meerdere gelijktijdige cursors:

Voor (pymssql):

c1 = conn.cursor()
c1.execute("SELECT TOP 5 * FROM Person.Person")
c2 = conn.cursor()
c2.execute("SELECT TOP 5 * FROM Sales.SalesOrderHeader")
c1.fetchall()

Na (mssql-python):

c1 = conn.cursor()
c1.execute("SELECT TOP 5 BusinessEntityID, FirstName FROM Person.Person")
persons = c1.fetchall()

c2 = conn.cursor()
c2.execute("SELECT TOP 5 SalesOrderID FROM Sales.SalesOrderHeader")
orders = c2.fetchall()
print(f"Persons: {len(persons)}, Orders: {len(orders)}")

Groepsgewijze verbindingen

PymsSQL heeft geen ingebouwde pooling. De mssql-python-driver bevat het automatisch:

Voor (pymssql, externe pool vereist):

from dbutils.pooled_db import PooledDB
pool = PooledDB(pymssql, host="server", user="user", password="pwd", database="db")
conn = pool.connection()

Daarna (mssql-python, pooling gaat automatisch):

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

Foutafhandeling

De mssql-python-driver gebruikt dezelfde uitzonderingshiërarchie als pymssql, dus de meeste uitzonderingshandlers vereisen alleen een modulenaamwijziging:

Voor (pymssql):

try:
    cursor.execute(query)
except pymssql.OperationalError as e:
    print(f"Operation failed: {e}")
except pymssql.InterfaceError as e:
    print(f"Interface error: {e}")

Na (mssql-python):

try:
    cursor = conn.cursor()
    cursor.execute("SELECT TOP 1 * FROM Production.Product")
    row = cursor.fetchone()
    print(row)
except mssql_python.OperationalError as e:
    print(f"Operation failed: {e}")
except mssql_python.InterfaceError as e:
    print(f"Interface error: {e}")

Voorbeeld van volledige migratie

Het volgende toont dezelfde functie geschreven met pymssql en vervolgens herschreven met mssql-python.

Daarvoor (pymssql)

Deze versie gebruikt positionele argumenten voor connect, as_dict=True en %d parametermarkeringen:

import pymssql

def get_orders(customer_id: int):
    conn = pymssql.connect("<server>", "<user>", "<password>", "<database>")
    cursor = conn.cursor(as_dict=True)

    cursor.execute("""
        SELECT TOP 10 SalesOrderID, OrderDate, TotalDue
        FROM Sales.SalesOrderHeader
        WHERE CustomerID = %d
        ORDER BY OrderDate DESC
    """, (customer_id,))

    orders = []
    for row in cursor:
        orders.append({
            "id": row["SalesOrderID"],
            "date": row["OrderDate"],
            "total": row["TotalDue"]
        })

    cursor.close()
    conn.close()
    return orders

Na (mssql-python)

De belangrijkste structurele veranderingen zijn parametermarkers, verbindingsstijl en rijtoegang:

import mssql_python

def get_orders(customer_id: int):
    conn = mssql_python.connect(
        "Server=<server>;"
        "Database=<database>;"
        "UID=<username>;"
        "PWD=<password>;"
    )
    cursor = conn.cursor()

    cursor.execute("""
        SELECT TOP 10 SalesOrderID, OrderDate, TotalDue
        FROM Sales.SalesOrderHeader
        WHERE CustomerID = ?
        ORDER BY OrderDate DESC
    """, (customer_id,))

    orders = []
    for row in cursor:
        orders.append({
            "id": row.SalesOrderID,
            "date": row.OrderDate,
            "total": row.TotalDue
        })

    cursor.close()
    conn.close()
    return orders

De structurele veranderingen zijn:

  1. import pymssqlimport mssql_python.
  2. Positionele verbindingsargumenten → verbindingsreeks met trefwoorden.
  3. %d parametermarker → ?.
  4. cursor(as_dict=True)cursor() met attribuuttoegang (row.SalesOrderID in plaats van row["SalesOrderID"]).

Controlelijst

  • [ ] Werk de importen bij van pymssql naar mssql_python.
  • [ ] Zet verbindingsoproepen om van positionele argumenten naar verbindingsstrings.
  • [ ] Converteer %s/%dparametermarkers naar ? of %(name)s.
  • [ ] Gebruik EXECUTE instructies voor aanroepen van opgeslagen procedures.
  • [ ] Gebruik Row attribuuttoegang in plaats van as_dict=True cursors.
  • [ ] Migreren conn.bulk_copy() naar cursor.bulkcopy().
  • [ ] Verwijder de externe verbindingspoolconfiguratie.
  • [ ] Verwijder FreeTDS uit de implementatievereisten.
  • [ ] Update klassennamen voor het afhandelen van uitzonderingen.
  • [ ] Test alle zoekopdrachten en opgeslagen procedures.