Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Il go-mssqldb driver supporta la chiamata di procedure memorizzate con parametri di input, parametri di output e valori di stato di ritorno. Questo articolo tratta i modelli più comuni.
La maggior parte dei frammenti di codice in questo articolo presuppone che la configurazione standard database/sql sia già presente, con database/sql importato come sql, un valore ctx disponibile e db inizializzato. Gli snippet includono blocchi di importazione solo quando introducono pacchetti aggiuntivi come context, fmt, log, os, o github.com/microsoft/go-mssqldb. Quando un blocco di codice continua intenzionalmente lo stesso esempio, usa = per valori già dichiarati in precedenza in quell'esempio; usa := per dichiarazioni nuove in snippet standalone.
Chiamare una procedura memorizzata
Usa ExecContext o QueryContext direttamente con il nome della procedura:
_, err := db.ExecContext(ctx, "dbo.uspGetEmployeeManagers",
sql.Named("BusinessEntityID", 6))
Preferisci il nome della procedura più i parametri per le chiamate ordinarie di procedure. Usa una stringa esplicita EXECUTE ... solo quando devi incorporare la chiamata in un batch più grande di Transact-SQL (T-SQL) oppure usa una sintassi che non è rappresentata da sql.Named parametri.
Parametri di output
Usa sql.Named con sql.Out per ricevere i valori dei parametri di uscita:
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)
La corrispondente procedura T-SQL:
CREATE PROCEDURE dbo.GetEmployeeCount
@count INT OUTPUT
AS
BEGIN
SELECT @count = COUNT(*) FROM HumanResources.Employee;
END;
Parametri di input/output
Per parametri che sono sia input che output, imposta In: true sulla sql.Out struct:
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)
Stato del reso
Usare mssql.ReturnStatus per catturare il valore di ritorno intero di una procedura memorizzata. Questo esempio utilizza la procedura AdventureWorks 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 funziona con ExecContext e QueryContext, ma non con QueryRowContext.
Puoi combinare ReturnStatus con i parametri della procedura. Questo esempio continua quello precedente, quindi riutilizza l'importazione mssql mostrata prima e utilizza la procedura AdventureWorks dbo.uspGetEmployeeManagers :
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()
Set di risultati dalle stored procedure
Se la procedura memorizzata restituisce insiemi di risultati, usa 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
}
Per procedure che restituiscono più set di risultati, si usa rows.NextResultSet(). Per ulteriori informazioni, vedi Query e dichiarazioni.
Leggi tutte le righe prima di utilizzare i parametri di output o lo stato di ritorno
SQL Server invia parametri di output e valori di stato di ritorno dopo la fine dei set di risultati. Se leggi una variabile di output prima di consumare tutte le righe, il valore può comunque essere incompleto.
Questo esempio continua quello precedente e riutilizza l'importazione mssql mostrata prima.
Non stampa i dati della riga. Il ciclo consuma solo il set dei risultati, quindi returnStatus è disponibile dopo la fine della query.
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)
Usalo ExecContext quando la procedura non restituisce righe. Se la procedura restituisce righe, attendi che tutte le righe e i set di risultati siano consumati prima di leggere i parametri di output o lo stato di restituzione.
Tabelle temporanee e procedure memorizzate
Quando crei una tabella temporanea e poi la interroghi in chiamate separate, le operazioni possono essere eseguite su connessioni diverse dal pool. Poiché le tabelle temporanee sono limitate a una singola connessione, la seconda chiamata potrebbe non vedere la tabella.
Per lavorare con tabelle temporanee, si utilizza un approccio a connessione singola:
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()
In alternativa, racchiudi le operazioni in una transazione, che le vincola automaticamente alla stessa connessione.
Cattura i messaggi di errore PRINT e RAISERROR
Le istruzioni SQL Server PRINT e RAISERROR con gravità 0-10 producono messaggi informativi che non vengono restituiti come errori Go. Per catturare questi messaggi, abilita il log parametro di connessione con il flag 2 (messaggi):
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=2
Con la registrazione abilitata, il driver scrive l'output e i messaggi RAISERROR di bassa gravità nel pacchetto standard log di Go. Per catturarli programmaticamente, imposta un logger personalizzato prima di aprire la connessione:
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")
Poi qualsiasi procedura memorizzata che utilizza PRINT o RAISERROR(..., 0, 1) invia i suoi messaggi al tuo logger.
Note
RAISERROR con un livello di gravità pari a 11 o superiore produce un errore Go che puoi gestire con i normali controlli degli errori. Solo i messaggi di gravità 0-10 richiedono il log parametro per essere catturati.
Per maggiori informazioni sui flag di logging e sulla cattura programmatica dei log, vedi Logging e diagnostica.