Voer queries uit met mssql-python

De mssql-python-driver biedt cursormethoden voor SQL-query-uitvoering, geparametriseerde queries, batchbewerkingen en prepared statements.

Uitvoering van basisquery

Gebruik de execute() cursormethode om SQL-instructies uit te voeren:

import mssql_python

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

cursor.execute("SELECT Name, ListPrice FROM Production.Product WHERE Color = 'Black'")
rows = cursor.fetchall()

for row in rows:
    print(row.Name, row.ListPrice)

cursor.close()
conn.close()

Geparameteriseerde query's

Gebruik altijd geparameteriseerde query's om SQL-injectie te voorkomen. De standaard paramstyle van de bestuurder is pyformat (benoemde placeholders), maar het ondersteunt qmark ook (positionele placeholders). Gebruik qmark voor ODBC-ontsnappingssequenties {CALL} .

Pyformat-stijl (standaard)

Gebruik benoemde placeholders met %(name)s syntaxis en geef een woordenboek door:

cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE Color = %(color)s AND ListPrice > %(price)s",
    {"color": "Black", "price": 10.00}
)

Qmark-stijl

Gebruik positionele placeholders met ? en geef een tuple of lijst door:

cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = ? AND ListPrice > ?",
    (1, 10.00)
)

De driver detecteert automatisch de parameterstijl op basis van je SQL-query en parametertypes.

INSERT, UPDATE, DELETE bewerkingen

Voor gegevenswijzigingsstatements gebruik je geparametriseerde queries en commit je de transactie:

cursor.execute("CREATE TABLE #ExecDemo (Name NVARCHAR(50), CategoryID INT, Price DECIMAL(10,2))")
cursor.execute(
    "INSERT INTO #ExecDemo (Name, CategoryID, Price) VALUES (%(name)s, %(category)s, %(price)s)",
    {"name": "New Product", "category": 1, "price": 19.99}
)
conn.commit()

print(f"Rows affected: {cursor.rowcount}")

Batch-uitvoering met executemany()

Gebruik executemany() het om efficiënt meerdere rijen in te voegen. De driver gebruikt kolomgewijze parameterbinding voor hoge prestaties:

products = [
    {"name": "Product A", "category": 1, "price": 10.00},
    {"name": "Product B", "category": 1, "price": 15.00},
    {"name": "Product C", "category": 2, "price": 20.00},
]

cursor.execute("CREATE TABLE #BatchDemo (Name NVARCHAR(50), CategoryID INT, Price DECIMAL(10,2))")
cursor.executemany(
    "INSERT INTO #BatchDemo (Name, CategoryID, Price) VALUES (%(name)s, %(category)s, %(price)s)",
    products
)
conn.commit()

print(f"Rows inserted: {cursor.rowcount}")

Met qmark-stijl:

products = [
    ("Product A", 1, 10.00),
    ("Product B", 1, 15.00),
    ("Product C", 2, 20.00),
]

cursor.execute("CREATE TABLE #QmarkDemo (Name NVARCHAR(50), CategoryID INT, Price DECIMAL(10,2))")
cursor.executemany(
    "INSERT INTO #QmarkDemo (Name, CategoryID, Price) VALUES (?, ?, ?)",
    products
)
conn.commit()

Batchuitvoering met meerdere instructies

Gebruik batch_execute() op de verbinding om meerdere verschillende statements in één aanroep uit te voeren:

results, cursor = conn.batch_execute(
    [
        "CREATE TABLE #BatchExec (Name NVARCHAR(50), CategoryID INT)",
        "INSERT INTO #BatchExec (Name, CategoryID) VALUES (%(name)s, %(cat)s)",
        "SELECT COUNT(*) FROM #BatchExec"
    ],
    [
        None,                                 # No params for CREATE
        {"name": "New Item", "cat": 1},       # Params for INSERT
        None                                  # No params for SELECT
    ]
)

print(f"CREATE result: {results[0]}")
print(f"INSERT affected: {results[1]} rows")
print(f"Row count: {results[2][0][0]}")

Voorbereide statements

De driver bereidt standaard queries voor (use_prepare=True). Wanneer je dezelfde SQL-string meerdere keren op dezelfde cursor uitvoert, hergebruikt de driver automatisch de voorbereide instructie bij volgende aanroepen:

# First execution prepares the statement
cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = %(subcategory_id)s",
    {"subcategory_id": 1},
)
rows1 = cursor.fetchall()

# Same SQL string on same cursor → driver reuses the prepared plan
cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = %(subcategory_id)s",
    {"subcategory_id": 2},
)
rows2 = cursor.fetchall()

Om de voorbereiding over te slaan en in plaats daarvan directe uitvoering te gebruiken:

cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = 1",
    use_prepare=False  # Uses SQLExecDirectW instead of SQLPrepareW
)

Uitvoeren op verbindingsniveau

Voor eenvoudige eenmalige query's gebruik je execute() rechtstreeks op de verbinding:

# Creates cursor, executes, and returns cursor
cursor = conn.execute("SELECT TOP 10 Name, ListPrice FROM Production.Product")
rows = cursor.fetchall()
cursor.close()

opgeslagen procedures

Roep opgeslagen procedures aan met behulp van EXECUTE of de ODBC-escape-syntaxis {CALL}. Voor informatie over uitvoerparameters, meerdere resultaatsets en transactiepatronen, zie Stored procedures.

cursor.execute(
    "EXECUTE dbo.uspGetManagerEmployees @BusinessEntityID = %(business_entity_id)s",
    {"business_entity_id": 16}
)
rows = cursor.fetchall()

Stel invoergroottes in

Gebruik setinputsizes() om expliciet parametertypes te declareren, wat de prestaties voor batchbewerkingen kan verbeteren:

cursor.setinputsizes([
    (mssql_python.SQL_WVARCHAR, 50, 0),   # NVARCHAR(50)
    (mssql_python.SQL_INTEGER, 0, 0),     # INT
])

cursor.executemany(
    "SELECT ProductID, Name FROM Production.Product WHERE Name LIKE ? AND ProductSubcategoryID = ?",
    [("Road%", 2), ("Mountain%", 1)]
)

Opmerking

Niet alle SQL-type constanten werken met setinputsizes(). SQL_WVARCHAR en SQL_INTEGER betrouwbaar zijn. Voor decimale waarden gebruik je de automatische typeinferentie van de bestuurder in plaats van SQL_DECIMAL, wat een bekend probleem heeft (GitHub #503).

Foutafhandeling

Plaats databasebewerkingen in try-except-blokken:

try:
    cursor.execute("CREATE TABLE #ErrDemo (Name NVARCHAR(50) NOT NULL)")
    cursor.execute("INSERT INTO #ErrDemo (Name) VALUES (%(name)s)", {"name": None})
    conn.commit()
except mssql_python.IntegrityError as e:
    print(f"Constraint violation: {e}")
    conn.rollback()
except mssql_python.ProgrammingError as e:
    print(f"SQL error: {e}")
    conn.rollback()

Beste praktijken

  1. Gebruik altijd geparametriseerde queries om SQL-injectie te voorkomen.
  2. Gebruik bulk copy voor bulkinvoegingen in plaats van meerdere execute() aanroepen.
  3. Bevestig transacties expliciet wanneer je autocommit uitschakelt.
  4. Sluit cursors en verbindingen wanneer je klaar bent om middelen vrij te maken.
  5. Gebruik contextmanagers voor het automatisch opschonen van resources:
with mssql_python.connect(connection_string) as conn:
    with conn.cursor() as cursor:
        cursor.execute("SELECT TOP 5 Name, ListPrice FROM Production.Product")
        rows = cursor.fetchall()
# Connection and cursor automatically closed