Módulo 2 · SQL Server / Procedures — Capítulo 04

Retorno, OUTPUT e Parâmetros Opcionais

Três formas de uma procedure "responder" para quem a chamou: RETURN, parâmetros OUTPUT e o result set do SELECT — e quando usar cada uma.

1. Três canais de retorno

Diferente de um método em C#, que tem um único valor de retorno, uma Stored Procedure tem três canais independentes para devolver informação:

CanalTipo de dadoUso típico
RETURNUm único INTCódigo de status (0 = sucesso, outro valor = erro específico)
Parâmetros OUTPUTQualquer tipo, múltiplos valoresDevolver um Id gerado, um total calculado, uma mensagem
SELECTUm conjunto de linhas (result set)Devolver dados para exibir em tela — o mais comum
Stored Procedure RETURN 0 @NovoId INT OUTPUT SELECT ... → código de status → valores calculados → linhas de dados

Fig. 1 — Os três canais de saída de uma procedure funcionam ao mesmo tempo, sem se excluir.

2. RETURN — código de status

RETURN aceita apenas um inteiro e encerra a procedure imediatamente nesse ponto — nenhum comando depois dele executa. A convenção mais comum: 0 para sucesso, qualquer outro valor identifica um tipo de falha.

SQL usando RETURN como código de status
CREATE PROCEDURE dbo.usp_ClienteExcluir
    @Id INT
AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS (SELECT 1 FROM dbo.Cliente WHERE Id = @Id)
    BEGIN
        RETURN 1; -- 1 = cliente não encontrado
    END

    UPDATE dbo.Cliente SET Ativo = 0 WHERE Id = @Id;
    RETURN 0; -- 0 = sucesso
END;
GO

-- capturando o código de retorno
DECLARE @StatusCode INT;
EXEC @StatusCode = dbo.usp_ClienteExcluir @Id = 42;

IF @StatusCode = 1
    PRINT 'Cliente não encontrado';
ELSE
    PRINT 'Excluído com sucesso';
Atenção

RETURN só aceita INT — não dá para devolver uma string ou um valor decimal por esse canal. Para qualquer outro tipo, use parâmetros OUTPUT.

3. Parâmetros OUTPUT

Um parâmetro marcado OUTPUT funciona como out em C#: a procedure escreve um valor nele, e quem chamou lê esse valor depois da execução.

SQL usp_ClienteInserir com OUTPUT
CREATE PROCEDURE dbo.usp_ClienteInserir
    @Nome    NVARCHAR(100),
    @Email   NVARCHAR(150),
    @NovoId  INT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO dbo.Cliente (Nome, Email)
    VALUES (@Nome, @Email);

    SET @NovoId = SCOPE_IDENTITY();
END;
GO

DECLARE @IdGerado INT;

EXEC dbo.usp_ClienteInserir
    @Nome   = 'Wellington Marunaka',
    @Email  = 'wellington@exemplo.com',
    @NovoId = @IdGerado OUTPUT;

PRINT 'Novo cliente criado com Id ' + CAST(@IdGerado AS VARCHAR(10));
Dica

Repare que OUTPUT aparece duas vezes: na declaração do parâmetro (@NovoId INT OUTPUT) e na chamada (@NovoId = @IdGerado OUTPUT). Esquecer o segundo OUTPUT não gera erro — a procedure roda, mas @IdGerado continua NULL do lado de fora. É um dos bugs mais silenciosos do T-SQL.

É possível combinar múltiplos parâmetros OUTPUT com RETURN na mesma procedure:

SQL combinando RETURN + múltiplos OUTPUT
CREATE PROCEDURE dbo.usp_ClienteInserir
    @Nome       NVARCHAR(100),
    @Email      NVARCHAR(150),
    @NovoId     INT OUTPUT,
    @Mensagem   NVARCHAR(200) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    IF EXISTS (SELECT 1 FROM dbo.Cliente WHERE Email = @Email)
    BEGIN
        SET @Mensagem = 'E-mail já cadastrado';
        RETURN 1;
    END

    INSERT INTO dbo.Cliente (Nome, Email)
    VALUES (@Nome, @Email);

    SET @NovoId   = SCOPE_IDENTITY();
    SET @Mensagem = 'Cliente criado com sucesso';
    RETURN 0;
END;
GO

4. Parâmetros opcionais com valor padrão

Você já viu isso no capítulo 03 (@Ativo BIT = NULL). A regra geral: todo parâmetro com = valor na declaração se torna opcional na chamada.

SQL parâmetros com valor padrão
CREATE PROCEDURE dbo.usp_ClienteInserir
    @Nome   NVARCHAR(100),
    @Email  NVARCHAR(150),
    @Ativo  BIT = 1          -- opcional, assume 1 (ativo) se omitido
AS
BEGIN
    SET NOCOUNT ON;
    INSERT INTO dbo.Cliente (Nome, Email, Ativo)
    VALUES (@Nome, @Email, @Ativo);
END;
GO

-- as duas chamadas abaixo são válidas
EXEC dbo.usp_ClienteInserir @Nome = 'A', @Email = 'a@x.com';
EXEC dbo.usp_ClienteInserir @Nome = 'B', @Email = 'b@x.com', @Ativo = 0;
Comparando com C#

Isso é o equivalente direto de um parâmetro opcional em C#: public void InserirCliente(string nome, string email, bool ativo = true). Mesma regra também vale em T-SQL: parâmetros obrigatórios (sem valor padrão) devem vir antes dos opcionais na lista de declaração.

5. Onde isso te leva

Agora você sabe fazer uma procedure "conversar" de volta com quem a chamou de três formas diferentes. O que ainda falta é a parte que protege tudo isso quando algo dá errado no meio do caminho — se um INSERT funciona mas um UPDATE relacionado falha, o banco não pode ficar num estado parcialmente atualizado. É exatamente o que transações resolvem, no próximo capítulo.

📌 Resumo do capítulo

  • Três canais de retorno: RETURN (um INT, código de status), parâmetros OUTPUT (qualquer tipo, múltiplos valores) e SELECT (conjunto de linhas).
  • OUTPUT precisa aparecer tanto na declaração do parâmetro quanto na chamada — esquecer na chamada não gera erro, só silenciosamente não devolve o valor.
  • Parâmetros com valor padrão (= valor) se tornam opcionais; devem vir depois dos obrigatórios.

✏️ Praticando

  1. Reescreva usp_ClienteAtualizar (capítulo 03) para devolver RETURN 1 quando o Id não existir e RETURN 0 em sucesso.
  2. Adicione um parâmetro @LinhasAfetadas INT OUTPUT na mesma procedure, preenchido com @@ROWCOUNT.
  3. Escreva o bloco de chamada em T-SQL que declara as variáveis, executa a procedure e imprime o resultado com PRINT.