Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
De mssql-python-driver implementeert de callproc() methode uit de DB-API 2.0-specificatie niet. Het aanroepen van callproc() veroorzaakt NotSupportedError. Gebruik in plaats daarvan de ODBC-escape-sequentie {CALL ...} met standaard query-uitvoeringsmethoden.
Basis uitvoering van opgeslagen procedures
Zonder parameters
Voer een opgeslagen procedure uit door gebruik te maken van de {CALL} escape-sequentie. Dit voorbeeld roept de sp_databases systeemstored procedure aan, die geen parameters neemt en één rij per database teruggeeft:
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)
Met invoerparameters
Geef parameters door met behulp van positionele of benoemde tijdelijke aanduidingen:
# Single parameter
cursor.execute(
"{CALL dbo.uspGetManagerEmployees(?)}", (16,)
)
for row in cursor:
print(row.FirstName, row.LastName)
Uitvoerparameters
Verklaar en haal outputparameters op
SQL Server stored procedures kunnen waarden teruggeven via outputparameters. Gebruik Transact-SQL (T-SQL) variabelen om uitvoerwaarden vast te leggen en haal ze vervolgens op met een SELECT instructie:
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}")
Meervoudige uitvoerparameters
Vang meerdere uitvoerwaarden vast door aparte variabelen te declareren. Hetzelfde patroon werkt met elke opgeslagen procedure die parameters heeft 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}")
Retourwaarden
Retourwaarde van de opgeslagen procedure vastleggen
Voer een opgeslagen procedure uit die resultaten teruggeeft:
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")
Retourwaarde met uitvoerparameters
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}")
Resultatensets
Eén resultatenset
Voer een opgeslagen procedure uit die één enkele resultaatset teruggeeft:
cursor.execute(
"{CALL dbo.uspGetBillOfMaterials(?, ?)}",
(800, "2026-01-01")
)
customers = cursor.fetchall()
for row in customers:
print(f"{row.ProductAssemblyID}: {row.ComponentDesc}")
Meerdere resultaatsets
Voor elk resultaat waar je naartoe gaat, nextset() ververst rowcount. Een data-wijzigingsresultaat rapporteert zijn eigen aantal getroffen rijen en heeft description is None; roep geen fetch-methoden aan voor dat resultaat. Een resultaat dat rij produceert begint met rowcount == -1 vóór het ophalen.
SET NOCOUNT ON onderdrukt resultaten van beïnvloede rijen. Voor een batch die deze resultaattypen combineert, zie Volg elk resultaat in een batch.
Sommige opgeslagen procedures retourneren meerdere resultatensets. Gebruik nextset() dit om ertussen te navigeren:
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}")
Controleer of er meer resultaatsets zijn
Itereer door alle resultaatsets die door een opgeslagen procedure worden teruggegeven met behulp van 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
Transacties met opgeslagen procedures
Expliciete transactiecontrole
Plaats meerdere aanroepen van opgeslagen procedures in een transactie om atomiciteit te garanderen:
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}")
Laat stored procedure transactie beheren
Als de opgeslagen procedure zijn eigen transacties afhandelt:
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")
Foutafhandeling
Fouten in opgeslagen procedures vangen
Uitzonderingen afhandelen die worden gegenereerd door opgeslagen procedures of Transact-SQL-instructies:
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}")
Leg PRINT-statements en informatieve berichten vast
SQL Server-instructies PRINT en RAISERROR met een ernstniveau lager dan 11 worden na het uitvoeren vastgelegd in cursor.messages. Elke inzending is een (message_type, message_text) tuple. Wanneer een PRINT vóór een resultaatset wordt uitgevoerd, vormt het een eigen resultaatset zonder rijen, dus lees dan eerst cursor.messages en roep vervolgens nextset() aan om de rijen te benaderen:
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}")
Wanneer een opgeslagen procedure PRINT berichten verzendt over meerdere resultaatsets, lees dan cursor.messages na execute() en opnieuw na elke nextset()-aanroep, zodat berichten uit elke resultaatset worden opgevangen:
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}")
Voor de volledige cursor.messages API, zie Cursorbeheer.
Beste praktijken
Gebruik benoemde parameters
Benoemde parameters zijn duidelijker en behouden de onafhankelijkheid van de orde:
# 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"))
Verwerk nullbare uitvoerparameters
Controleer op NULL-waarden bij het ophalen van uitvoerparameters uit opgeslagen procedures:
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")
Gebruik SET NOCOUNT ON in opgeslagen procedures
Voor betere prestaties en een nettere verwerking van resultaten, zorg ervoor dat uw opgeslagen procedures het volgende omvatten:
CREATE PROCEDURE dbo.MyProcedure
AS
BEGIN
SET NOCOUNT ON; -- Prevents "n rows affected" messages
-- procedure logic
END
Voorbeeld: Volledige workflow
Hier is een praktisch voorbeeld dat een opgeslagen procedure aanroept, uitvoerwaarden ophaalt en fouten 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)
Genereer sleutels door gebruik te maken van OUTPUT INSERTED
Om een identiteitswaarde op te halen uit een INSERT (met of zonder opgeslagen procedure), gebruik OUTPUT INSERTED in plaats van SCOPE_IDENTITY(). Deze benadering geeft de waarde terug in dezelfde resultaatset:
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}")
Dit patroon werkt voor elke tabel met een identiteitskolom en vereist geen opgeslagen procedure.
Niet-ondersteunde functies
callproc()
De mssql-python chauffeur verhoogt NotSupportedError als je belt cursor.callproc(). Gebruik cursor.execute("{CALL ...}") of cursor.execute("EXECUTE ...") in plaats daarvan, zoals in dit artikel wordt getoond.
Parameters met tabelwaarde (TVP's)
De mssql-python driver ondersteunt geen tabelwaardige parameters. Voor de volledige feature-ondersteuningsmatrix, zie Support lifecycle. Als je een set rijen moet doorgeven aan een opgeslagen procedure, gebruik dan deze alternatieven:
- Plaats het eerst in een tijdelijke tabel en laat de opgeslagen procedure daar vervolgens uit lezen.
- Gebruik
bulkcopy()om data in een stagingtabel te laden. - Geef een JSON-string door en parseer deze met
OPENJSONbinnen de procedure.