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 Treiber go-mssqldb unterstützt das Aufrufen gespeicherter Prozeduren mit Eingabeparametern, Ausgabeparametern und Rückgabestatuswerten. Dieser Artikel behandelt die häufigsten Muster.
Die meisten Codebeispiele in diesem Artikel setzen voraus, dass das übliche database/sql-Setup bereits vorhanden ist, wobei database/sql als sql importiert wurde, ein ctx-Wert verfügbar ist und db initialisiert wurde. Snippets enthalten Importblöcke nur, wenn sie zusätzliche Pakete wie context, fmt, , log, osoder github.com/microsoft/go-mssqldbeinführen. Wenn ein Codeblock absichtlich dasselbe Beispiel fortsetzt, verwenden = Sie für Werte, die bereits zuvor in diesem Beispiel deklariert wurden; verwenden := Sie für frische Deklarationen in eigenständigen Snippets.
Aufrufen einer gespeicherten Prozedur
Verwenden Sie ExecContext oder QueryContext direkt mit dem Verfahrensnamen:
_, err := db.ExecContext(ctx, "dbo.uspGetEmployeeManagers",
sql.Named("BusinessEntityID", 6))
Bevorzuge den Prozedurnamen plus Parameter für gewöhnliche Prozeduraufrufe. Verwenden Sie eine explizite EXECUTE ... Zeichenkette nur, wenn Sie den Aufruf in einen größeren Transact-SQL (T-SQL) Batch einbetten oder eine Syntax verwenden müssen, die nicht durch sql.Named Parameter dargestellt wird.
Ausgabeparameter
Verwenden Sie sql.Named mit sql.Out, um die Werte von Ausgabeparametern zu empfangen:
import (
"context"
"database/sql"
"fmt"
"log"
)
ctx := context.Background()
var employeeCount int64
_, err := db.ExecContext(ctx, "dbo.GetEmployeeCount",
sql.Named("count", sql.Out{Dest: &employeeCount}))
if err != nil {
log.Fatal(err)
}
fmt.Println("Employee count:", employeeCount)
Das entsprechende T-SQL-Verfahren:
CREATE PROCEDURE dbo.GetEmployeeCount
@count INT OUTPUT
AS
BEGIN
SELECT @count = COUNT(*) FROM HumanResources.Employee;
END;
Eingabe-/Ausgabeparameter
Für Parameter, die sowohl Eingabe als auch Ausgabe sind, legen Sie In: true in der sql.Out-Struktur fest:
var result int64 = 10
_, err := db.ExecContext(ctx, "dbo.DoubleValue",
sql.Named("value", sql.Out{Dest: &result, In: true}))
if err != nil {
log.Fatal(err)
}
fmt.Println("Doubled:", result)
Status zurückgeben
Verwenden Sie, mssql.ReturnStatus um den ganzzahligen Rückgabewert einer gespeicherten Prozedur abzufangen. Dieses Beispiel verwendet das AdventureWorks-Verfahren dbo.uspGetEmployeeManagers :
import (
"database/sql"
"fmt"
"log"
mssql "github.com/microsoft/go-mssqldb"
)
var returnStatus mssql.ReturnStatus
rows, err := db.QueryContext(ctx, "dbo.uspGetEmployeeManagers",
sql.Named("BusinessEntityID", 6),
&returnStatus)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
fmt.Println("Return status:", returnStatus)
ReturnStatus funktioniert mit ExecContext und QueryContext, aber nicht mit QueryRowContext.
Du kannst sie mit Verfahrensparametern kombinieren ReturnStatus . Dieses Beispiel setzt das vorherige Beispiel fort, sodass der zuvor gezeigte Import mssql wiederverwendet wird und das AdventureWorks-Verfahren dbo.uspGetEmployeeManagers verwendet wird:
var returnStatus mssql.ReturnStatus
rows, err := db.QueryContext(ctx, "dbo.uspGetEmployeeManagers",
sql.Named("BusinessEntityID", 6),
&returnStatus)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
Ergebnismengen aus gespeicherten Prozeduren
Wenn die gespeicherte Prozedur Ergebnismengen zurückgibt, verwenden Sie QueryContext:
rows, err := db.QueryContext(ctx, "dbo.uspGetEmployeeManagers", sql.Named("BusinessEntityID", 6))
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var reportingLevel int
var employeeID int
var employeeFirstName string
var employeeLastName string
var organizationNode string
var managerFirstName string
var managerLastName string
if err := rows.Scan(&reportingLevel, &employeeID, &employeeFirstName, &employeeLastName, &organizationNode, &managerFirstName, &managerLastName); err != nil {
log.Fatal(err)
}
fmt.Printf("Level %d: %d %s %s (manager: %s %s)\n",
reportingLevel, employeeID, employeeFirstName, employeeLastName, managerFirstName, managerLastName)
_ = organizationNode
}
Für Prozeduren, die mehrere Ergebnismengen zurückgeben, verwenden Sie rows.NextResultSet(). Weitere Informationen finden Sie unter Anfragen und Aussagen.
Lesen Sie alle Zeilen, bevor Sie Ausgabeparameter oder Rückgabestatus verwenden
SQL Server sendet Ausgabeparameter und Rückgabestatuswerte erst, nachdem die Ergebnismengen abgeschlossen sind. Wenn du eine Ausgabevariable liest, bevor du alle Zeilen verbrauchst, kann der Wert trotzdem unvollständig sein.
Dieses Beispiel setzt das vorherige fort und verwendet den zuvor gezeigten mssql Import wieder.
Die Zeilendaten werden nicht gedruckt. Die Schleife verbraucht nur die Ergebnismenge und returnStatus ist nach Abschluss der Abfrage verfügbar.
var returnStatus mssql.ReturnStatus
rows, err := db.QueryContext(ctx, "dbo.uspGetEmployeeManagers",
sql.Named("BusinessEntityID", 6),
&returnStatus)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var reportingLevel int
var employeeID int
var employeeFirstName string
var employeeLastName string
var organizationNode string
var managerFirstName string
var managerLastName string
if err := rows.Scan(&reportingLevel, &employeeID, &employeeFirstName, &employeeLastName, &organizationNode, &managerFirstName, &managerLastName); err != nil {
log.Fatal(err)
}
_ = organizationNode
_ = managerFirstName
_ = managerLastName
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
fmt.Println("Return status:", returnStatus)
Verwenden Sie ExecContext, wenn die Prozedur keine Zeilen zurückgibt. Wenn das Verfahren Zeilen zurückgibt, warten Sie, bis alle Zeilen und Ergebnismengen vollständig verarbeitet wurden, bevor Sie die Ausgabeparameter oder den Rückgabestatus lesen.
Temporäre Tabellen und gespeicherte Prozeduren
Wenn du eine temporäre Tabelle erstellst und sie dann in separaten Aufrufen abfraibst, können die Operationen auf verschiedenen Verbindungen aus dem Pool ausgeführt werden. Da temporäre Tabellen auf eine einzelne Verbindung beschränkt sind, sieht der zweite Anruf möglicherweise die Tabelle nicht.
Um mit temporären Tabellen zu arbeiten, verwenden Sie einen Single-Connection-Ansatz:
conn, err := db.Conn(ctx)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
_, err = conn.ExecContext(ctx, "CREATE TABLE #TempItems (Id INT, Name NVARCHAR(50))")
if err != nil {
log.Fatal(err)
}
_, err = conn.ExecContext(ctx, "INSERT INTO #TempItems VALUES (1, N'Item A')")
if err != nil {
log.Fatal(err)
}
rows, err := conn.QueryContext(ctx, "SELECT * FROM #TempItems")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
Alternativ kann man die Operationen in eine Transaktion einwickeln, die sie automatisch an dieselbe Verbindung anheftet.
Erfassen Sie PRINT- und RAISERROR-Nachrichten
SQL Server-Anweisungen PRINT und RAISERROR mit Schweregrad 0-10 erzeugen Informationsmeldungen, die nicht als Go-Fehler zurückgegeben werden. Um diese Nachrichten zu erfassen, aktivieren Sie den log Verbindungsparameter mit Flag 2 (Nachrichten):
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=2
Bei aktiviertem Logging schreibt der Treiber PRINT Ausgabe und Meldungen mit niedrigem Schweregrad RAISERROR in Gos Standardpaket log. Um sie programmatisch zu erfassen, richten Sie vor Öffnen der Verbindung einen benutzerdefinierten Logger ein:
import (
"database/sql"
"log"
"os"
mssql "github.com/microsoft/go-mssqldb"
)
// Direct driver messages to a custom logger.
mssql.SetLogger(log.New(os.Stdout, "mssql: ", log.LstdFlags))
db, err := sql.Open("sqlserver",
"sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=2")
Dann sendet jede gespeicherte Prozedur, die PRINT oder RAISERROR(..., 0, 1) verwendet, ihre Nachrichten an deinen Logger.
Hinweis
RAISERROR mit Fehlergrad 11 oder höher erzeugt einen Fehler in Go, den du mit der normalen Fehlerprüfung behandeln kannst. Nur Nachrichten mit einem Schweregrad von 0 bis 10 erfordern den Parameter log für die Erfassung.
Weitere Informationen zu Logging-Flags und programmatischer Logermittlung finden Sie unter Logging und Diagnostik.