Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Der mssql-python-Treiber implementiert die callproc() Methode aus der DB-API 2.0-Spezifikation nicht. Der Aufruf von callproc() löst NotSupportedError aus. Verwenden Sie stattdessen die ODBC-Escape-Sequenz {CALL ...} mit Standardmethoden zur Abfrageausführung.
Grundlegende Ausführung gespeicherter Prozeduren
Ohne Parameter
Führen Sie eine gespeicherte Prozedur mit der {CALL} Escape-Sequenz aus. Dieses Beispiel ruft die systemgespeicherte sp_databases Prozedur auf, die keine Parameter annimmt und pro Datenbank eine Zeile zurückgibt:
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
cursor.execute("{CALL sp_databases}")
for row in cursor:
print(row.DATABASE_NAME, row.DATABASE_SIZE)
Mit Eingabeparametern
Passparameter mit positionsbezogenen oder benannten Platzhaltern:
# Single parameter
cursor.execute(
"{CALL dbo.uspGetManagerEmployees(?)}", (16,)
)
for row in cursor:
print(row.FirstName, row.LastName)
Ausgabeparameter
Ausgabeparameter deklarieren und abrufen
SQL Server gespeicherte Prozeduren können Werte über Ausgabeparameter zurückgeben. Verwenden Sie Transact-SQL (T-SQL)-Variablen, um Ausgabewerte zu erfassen, und rufen Sie sie dann mit einer Anweisung SELECT ab:
cursor.execute("""
DECLARE @total_out MONEY;
SELECT @total_out = SUM(TotalDue)
FROM Sales.SalesOrderHeader
WHERE CustomerID = %(customer_id)s;
SELECT @total_out AS TotalAmount;
""", {"customer_id": 29825})
row = cursor.fetchone()
total = row.TotalAmount
print(f"Customer total: ${total}")
Mehrere Ausgangsparameter
Erfassen Sie mehrere Ausgabewerte, indem Sie separate Variablen deklarieren. Das gleiche Muster funktioniert mit jedem gespeicherten Verfahren, das Parameter hat OUTPUT :
cursor.execute("""
DECLARE @total_orders INT, @total_spent MONEY;
SELECT @total_orders = COUNT(*), @total_spent = SUM(TotalDue)
FROM Sales.SalesOrderHeader
WHERE CustomerID = %(cust_id)s;
SELECT @total_orders AS OrderCount, @total_spent AS TotalSpent;
""", {"cust_id": 29825})
stats = cursor.fetchone()
print(f"Orders: {stats.OrderCount}, Total spent: ${stats.TotalSpent}")
Rückgabewerte
Rückgabewert der gespeicherten Prozedur erfassen
Führe ein gespeichertes Verfahren aus, das Ergebnisse zurückgibt:
cursor.execute("EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(emp_id)s", {"emp_id": 5})
rows = cursor.fetchall()
if rows:
print(f"Found {len(rows)} managers in chain")
for row in rows:
print(f" Manager: {row.FirstName} {row.LastName}")
else:
print("No managers found")
Rückgabewert mit Ausgabeparametern
cursor.execute("""
DECLARE @return_value INT, @message NVARCHAR(500);
SELECT @return_value = CASE WHEN COUNT(*) > 0 THEN 0 ELSE 1 END,
@message = CASE WHEN COUNT(*) > 0 THEN N'Customer found' ELSE N'Customer not found' END
FROM Sales.Customer WHERE CustomerID = %(cust_id)s;
SELECT @return_value AS ReturnCode, @message AS Message;
""", {"cust_id": 29825})
result = cursor.fetchone()
print(f"Return code: {result.ReturnCode}, Message: {result.Message}")
Ergebnismengen
Einzelne Ergebnismenge
Führe eine gespeicherte Prozedur aus, die eine einzelne Ergebnismenge zurückgibt:
cursor.execute(
"{CALL dbo.uspGetBillOfMaterials(?, ?)}",
(800, "2026-01-01")
)
customers = cursor.fetchall()
for row in customers:
print(f"{row.ProductAssemblyID}: {row.ComponentDesc}")
Mehrere Ergebnismengen
Einige gespeicherte Prozeduren geben mehrere Resultsets zurück. Verwenden Sie nextset(), um zwischen ihnen zu navigieren:
cursor.execute("""
SELECT TOP 1 SalesOrderID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader WHERE CustomerID = 29825;
SELECT TOP 3 Name, ListPrice
FROM Production.Product WHERE ListPrice > 0
ORDER BY ListPrice DESC;
""")
# First result set: order header
order = cursor.fetchone()
print(f"Order: {order.SalesOrderID}, Date: {order.OrderDate}")
cursor.nextset()
# Second result set: products
print("Products:")
for item in cursor:
print(f" {item.Name}: ${item.ListPrice}")
Suchen Sie nach weiteren Ergebnismengen
Durchlaufen Sie alle von einer gespeicherten Prozedur zurückgegebenen Ergebnismengen mit nextset():
cursor.execute("SELECT TOP 3 ProductID, Name FROM Production.Product; SELECT TOP 3 FirstName, LastName FROM Person.Person")
result_set_num = 1
while True:
print(f"--- Result Set {result_set_num} ---")
for row in cursor:
print(row)
if not cursor.nextset():
break
result_set_num += 1
Transaktionen mit gespeicherten Verfahren
Explizite Transaktionskontrolle
Mehrere gespeicherte Prozeduraufrufe in einer Transaktion einwickeln, um die Atomität sicherzustellen:
conn.autocommit = False
try:
cursor.execute("{CALL dbo.DebitAccount(?, ?)}", (1001, 100.00))
cursor.execute("{CALL dbo.CreditAccount(?, ?)}", (1002, 100.00))
conn.commit()
print("Transfer completed")
except mssql_python.DatabaseError as e:
conn.rollback()
print(f"Transfer failed: {e}")
Lassen Sie die gespeicherte Prozedur die Transaktion verwalten
Wenn das gespeicherte Verfahren seine eigenen Transaktionen abwickelt:
conn.autocommit = True # Let SP manage transactions
cursor.execute("""
DECLARE @result INT;
EXECUTE @result = dbo.TransferFunds
@FromAccount = %(from_acc)s,
@ToAccount = %(to_acc)s,
@Amount = %(amount)s;
SELECT @result AS TransferResult;
""", {"from_acc": 1001, "to_acc": 1002, "amount": 100.00})
result = cursor.fetchone()
if result.TransferResult == 0:
print("Transfer successful")
Fehlerbehandlung
Fehler in gespeicherten Prozeduren abfangen
Behandle Ausnahmen, die durch gespeicherte Prozeduren oder Transact-SQL-Anweisungen ausgelöst werden:
try:
cursor.execute("{CALL dbo.DangerousProcedure}")
except mssql_python.ProgrammingError as e:
# Handle SQL errors raised by RAISERROR or THROW
print(f"Stored procedure error: {e}")
except mssql_python.DatabaseError as e:
# Handle other database errors
print(f"Database error: {e}")
Erfassen Sie PRINT-Anweisungen und Informationsnachrichten
SQL Server-Anweisungen PRINT und RAISERROR mit einem Schweregrad unter 11 werden nach der Ausführung in cursor.messages erfasst. Jeder Eintrag ist ein (message_type, message_text)-Tupel. Wenn eine PRINT vor einer Ergebnismenge ausgeführt wird, belegt sie eine eigene, zeilenlose Ergebnismenge. Lesen Sie daher zuerst cursor.messages und rufen Sie dann nextset() auf, um zu den Zeilen zu gelangen:
cursor.execute("PRINT 'Operation complete'; SELECT 1 AS Status")
for msg_type, msg_text in cursor.messages:
print(f"Server message: {msg_text}")
cursor.nextset()
row = cursor.fetchone()
print(f"Status: {row.Status}")
Wenn eine gespeicherte Prozedur MeldungenPRINT über mehrere Ergebnismengen ausgibt, lesen Sie cursor.messages nach execute() und erneut nach jedem Aufruf von nextset(), sodass Meldungen aus jeder Ergebnismenge erfasst werden:
cursor.execute("""
PRINT 'Starting first result set';
SELECT TOP 3 ProductID, Name FROM Production.Product;
PRINT 'Starting second result set';
SELECT TOP 3 FirstName, LastName FROM Person.Person;
""")
all_messages = []
while True:
all_messages.extend(cursor.messages)
if cursor.description:
for row in cursor:
print(row)
if not cursor.nextset():
break
for _, text in all_messages:
print(f"Server: {text}")
Für die vollständige cursor.messages API siehe Cursor-Management.
Bewährte Methoden
Verwendung benannter Parameter
Benannte Parameter sind klarer und bewahren die Ordnungsunabhängigkeit:
# Recommended: {CALL} with positional parameters
cursor.execute(
"{CALL dbo.uspGetBillOfMaterials(?, ?)}",
(800, "2026-01-01")
)
# Also valid: EXECUTE with named T-SQL parameters
cursor.execute("""
EXECUTE dbo.uspGetBillOfMaterials
@StartProductID = ?,
@CheckDate = ?
""", (800, "2026-01-01"))
Handhabung nullabler Ausgabeparameter
Überprüfen Sie NULL-Werte beim Abruf von Ausgabeparametern aus gespeicherten Prozeduren:
cursor.execute("""
DECLARE @optional_value NVARCHAR(100);
SELECT @optional_value = Color FROM Production.Product WHERE ProductID = %(id)s;
SELECT @optional_value AS OutputValue;
""", {"id": 1})
result = cursor.fetchone()
if result.OutputValue is not None:
print(f"Value: {result.OutputValue}")
else:
print("No value returned")
Verwenden von SET NOCOUNT ON in gespeicherten Prozeduren
Für eine bessere Leistung und eine sauberere Ergebnisbehandlung, stellen Sie sicher, dass Ihre gespeicherten Verfahren folgendes umfassen:
CREATE PROCEDURE dbo.MyProcedure
AS
BEGIN
SET NOCOUNT ON; -- Prevents "n rows affected" messages
-- procedure logic
END
Beispiel: Vollständiger Arbeitsablauf
Hier ist ein praktisches Beispiel, das eine gespeicherte Prozedur aufruft, Ausgabewerte abruft und Fehler behandelt:
import mssql_python
def get_employee_report(manager_id: int) -> dict:
"""Look up a manager's employees and compute average vacation hours."""
conn = mssql_python.connect(connection_string)
conn.autocommit = False
cursor = conn.cursor()
try:
# Get manager info
cursor.execute("""
SELECT BusinessEntityID, JobTitle
FROM HumanResources.Employee
WHERE BusinessEntityID = %(mgr)s
""", {"mgr": manager_id})
mgr = cursor.fetchone()
print(f"Manager {mgr.BusinessEntityID}: {mgr.JobTitle}")
# Get direct reports via stored procedure
cursor.execute("{CALL dbo.uspGetManagerEmployees(?)}", (manager_id,))
employees = cursor.fetchall()
print(f"Found {len(employees)} employee(s)")
# Compute average vacation hours
cursor.execute("""
DECLARE @avg_hours INT;
SELECT @avg_hours = AVG(VacationHours)
FROM HumanResources.Employee;
SELECT @avg_hours AS AvgVacation;
""")
avg = cursor.fetchone().AvgVacation
print(f"Avg vacation hours: {avg}")
conn.commit()
return {"manager": mgr.JobTitle, "reports": len(employees), "avg_vacation": avg}
except mssql_python.DatabaseError as e:
conn.rollback()
raise
finally:
cursor.close()
conn.close()
# Usage
result = get_employee_report(manager_id=16)
Generierte Schlüssel mithilfe von OUTPUT INSERTED abrufen
Um einen Identitätswert aus einem INSERT abzurufen (mit oder ohne gespeicherte Prozedur), verwenden Sie OUTPUT INSERTED anstelle von SCOPE_IDENTITY(). Dieser Ansatz gibt den Wert im selben Ergebnissatz zurück:
cursor.execute("""
INSERT INTO Production.ProductCategory (Name)
OUTPUT INSERTED.ProductCategoryID
VALUES (%(name)s)
""", {"name": "Custom Parts"})
new_id = cursor.fetchval()
print(f"New category ID: {new_id}")
Dieses Muster funktioniert für jede Tabelle mit einer Identitätsspalte und erfordert keine gespeicherte Prozedur.
Nicht unterstützte Funktionen
callproc()
Der mssql-python Treiber löst NotSupportedError aus, wenn Sie cursor.callproc() aufrufen. Verwenden Sie stattdessen cursor.execute("{CALL ...}") oder cursor.execute("EXECUTE ..."), wie in diesem Artikel durchgängig gezeigt.
Tabellenwertparameter (TVPs)
Tabellenwerte Parameter werden in der aktuellen Version (1.11.0) von mssql-pythonnicht unterstützt. Wenn Sie eine Reihe von Zeilen an eine gespeicherte Prozedur übergeben müssen, verwenden Sie Alternativen:
- Füge zuerst in eine Temp-Tabelle ein und lass dann die gespeicherte Prozedur daraus lesen.
- Verwenden Sie
bulkcopy(), um Daten in eine Stagingtabelle zu laden. - Übergeben Sie einen JSON-String und parsen Sie ihn innerhalb der Prozedur mit
OPENJSON.