Abfragen mit mssql-python ausführen

Der mssql-python-Treiber bietet Cursor-Methoden für SQL-Abfrageausführung, parametrisierte Abfragen, Batch-Operationen und vorbereitete Anweisungen.

Grundlegende Abfrageausführung

Verwenden Sie die execute() Methode eines Cursors, um SQL-Anweisungen auszuführen:

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

Parametrisierte Abfragen

Verwenden Sie parametrisierte Abfragen immer, um die SQL-Einfügung zu verhindern. Der standardmäßige Paramstyle des Treibers ist pyformat (benannte Platzhalter), er unterstützt aber auch qmark (positionale Platzhalter). Verwenden Sie qmark für ODBC-{CALL}Escape-Sequenzen.

Pyformat-Stil (Standard)

Verwenden Sie benannte Platzhalter mit der %(name)s-Syntax und übergeben Sie ein Wörterbuch:

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

Qmark-Stil

Verwenden Sie positionsbezogene Platzhalter mit ? und passieren Sie ein Tupel oder eine Liste:

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

Der Treiber erkennt automatisch den Parameterstil basierend auf deiner SQL-Abfrage und den Parametertypen.

INSERT, UPDATE, DELETE Operationen

Für Datenänderungsanweisungen verwenden Sie parametrisierte Abfragen und committen Sie die Transaktion:

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-Ausführung mit Executemany()

Verwenden Sie executemany(), um mehrere Zeilen effizient einzufügen. Der Treiber verwendet spaltenweise Parameterbindung für hohe Leistung:

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

Mit qmark-Stil:

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

Batch-Ausführung mehrerer Anweisungen

Wenn Sie einen aus mehreren Anweisungen bestehenden Transact-SQL (T-SQL)-Batch an cursor.execute() übergeben, rufen Sie nextset() auf, um durch die Ergebnisse zu navigieren. Bei jedem erfolgreichen Aufruf wird cursor.rowcount für dieses Ergebnis aktualisiert. Überprüfen Sie cursor.description vor dem Abruf, da die Ergebnisse von INSERT, UPDATE und DELETE die Anzahl der betroffenen Zeilen enthalten, anstatt die Zeilen selbst. Für weitere Informationen siehe Jedes Ergebnis in einem Batch nachverfolgen.

Verwenden Sie batch_execute() für die Verbindung, um mehrere verschiedene Anweisungen in einem einzigen Aufruf auszuführen:

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

Vorbereitete Anweisungen

Der Treiber bereitet standardmäßig Abfragen vor (use_prepare=True). Wenn du denselben SQL-String mehrfach auf demselben Cursor ausführst, verwendet der Treiber die vorbereitete Anweisung automatisch bei nachfolgenden Aufrufen:

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

Um die Vorbereitung zu überspringen und stattdessen direkte Ausführung zu verwenden:

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

Ausführung auf Verbindungsebene

Für einfache einmalige Abfragen verwenden Sie execute() direkt auf der Verbindung:

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

Gespeicherte Prozeduren

Rufen Sie gespeicherte Prozeduren mit EXECUTE oder der ODBC-Escape-Syntax {CALL} auf. Informationen zu Ausgabeparametern, mehreren Ergebnismengen und Transaktionsmustern finden Sie unter Gespeicherte Prozeduren.

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

Eingabegrößen festlegen

Verwenden Sie setinputsizes() , um Parametertypen explizit zu deklarieren, was die Leistung für Batch-Operationen verbessern kann:

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

Note

Nicht alle SQL-Typkonstanten funktionieren mit setinputsizes(). SQL_WVARCHAR und SQL_INTEGER zuverlässig sind. Für Dezimalwerte verwenden Sie die automatische Typinferenz des Treibers anstelle von SQL_DECIMAL, da hierfür ein bekanntes Problem besteht (GitHub #503).

Fehlerbehandlung

Datenbankoperationen in Try-Except-Blöcke einschließen:

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

Bewährte Methoden

  1. Verwenden Sie immer parametrisierte Abfragen, um SQL-Injektionen zu verhindern.
  2. Verwenden Sie bulk copy für Masseneinfügungen anstelle mehrerer execute()-Aufrufe.
  3. Bestätigen Sie Transaktionen explizit, wenn Sie den Autocommit-Modus deaktivieren.
  4. Schließen Sie Cursor und Verbindungen , wenn Sie fertig sind, um Ressourcen freizusetzen.
  5. Verwenden Sie Kontextmanager für die automatische Ressourcenbereinigung:
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