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

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