Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O go-mssqldb driver suporta chamar procedimentos armazenados com parâmetros de entrada, parâmetros de saída e valores de status de retorno. Este artigo aborda os padrões comuns.
A maioria dos trechos deste artigo pressupõe que a configuração usual database/sql já esteja configurada, com database/sql importado como sql, ctx disponível e db inicializado. Snippets incluem blocos de importação apenas quando introduzem pacotes adicionais como context, fmt, log, os, ou github.com/microsoft/go-mssqldb. Quando um bloco de código continua intencionalmente o mesmo exemplo, use = para valores que já foram declarados anteriormente nesse exemplo; use := para declarações novas em trechos independentes.
Chamar um procedimento armazenado
Use ExecContext ou QueryContext com o nome do procedimento diretamente:
_, err := db.ExecContext(ctx, "dbo.uspGetEmployeeManagers",
sql.Named("BusinessEntityID", 6))
Prefira o nome do procedimento seguido dos parâmetros para chamadas comuns de procedimento. Use uma string explícita EXECUTE ... apenas quando precisar incorporar a chamada em um lote maior de Transact-SQL (T-SQL) ou use sintaxe que não seja representada por sql.Named parâmetros.
Parâmetros de saída
Use sql.Named com sql.Out para receber valores dos parâmetros de saída:
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)
O procedimento correspondente do T-SQL:
CREATE PROCEDURE dbo.GetEmployeeCount
@count INT OUTPUT
AS
BEGIN
SELECT @count = COUNT(*) FROM HumanResources.Employee;
END;
Parâmetros de entrada/saída
Para parâmetros que são tanto entrada quanto saída, defina In: true na 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)
Status de retorno
Use mssql.ReturnStatus para capturar o valor inteiro de retorno de um procedimento armazenado. Este exemplo usa o procedimento 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 funciona com ExecContext e QueryContext, mas não com QueryRowContext.
Você pode combinar ReturnStatus com os parâmetros do procedimento. Este exemplo continua o exemplo anterior, então ele reutiliza a mssql importação mostrada anteriormente e usa o procedimento 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()
Conjuntos de resultados a partir de procedimentos armazenados
Se o procedimento armazenado retorna conjuntos de resultados, use 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
}
Para procedimentos que retornam múltiplos conjuntos de resultados, use rows.NextResultSet(). Para mais informações, veja Consultas e declarações.
Leia todas as linhas antes de usar parâmetros de saída ou status de retorno
O SQL Server envia parâmetros de saída e valores de status de retorno após o término dos conjuntos de resultados. Se você ler uma variável de saída antes de consumir todas as linhas, o valor ainda pode estar incompleto.
Este exemplo continua o anterior e reutiliza a mssql importação mostrada anteriormente.
Ele não imprime os dados da linha. O loop consome apenas o conjunto de resultados, então returnStatus fica disponível após o término da consulta.
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)
ExecContext Use quando o procedimento não retorna linhas. Se o procedimento retornar linhas, espere até que todas as linhas e os conjuntos de resultados sejam consumidos antes de ler os parâmetros de saída ou o status de retorno.
Tabelas temporárias e procedimentos armazenados
Quando você cria uma tabela temporária e depois faz consultas nela em chamadas separadas, as operações podem ser executadas em conexões diferentes do pool. Como as tabelas temporárias são restritas a uma única conexão, a segunda chamada pode não ver a tabela.
Para trabalhar com tabelas temporárias, use uma abordagem de conexão única:
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()
Alternativamente, encapsule as operações em uma transação, que as fixa automaticamente na mesma conexão.
Capturar mensagens PRINT e RAISERROR
As instruções do SQL Server PRINT eRAISERROR, com gravidade de 0 a 10, geram mensagens informativas que não são retornadas como erros de Go. Para capturar essas mensagens, ative o log parâmetro de conexão com a flag 2 (mensagens):
sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=2
Com o logging ativado, o driver grava a saída PRINT e mensagens RAISERROR de baixa gravidade no pacote log padrão do Go. Para capturá-los programaticamente, defina um logger personalizado antes de abrir a conexão:
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")
Então, qualquer procedimento armazenado que use PRINT ou RAISERROR(..., 0, 1) envia suas mensagens para o seu registrador.
Note
RAISERROR de gravidade 11 ou superior gera um erro em Go que você pode tratar com a verificação normal de erros. Apenas mensagens de gravidade 0-10 exigem o log parâmetro para serem capturadas.
Para mais informações sobre sinalizadores de registro e captura programática de logs, consulte Registro em log e diagnósticos.