[Acabei de perceber que já respondi essa pergunta antes]
Fazer isso para um procedimento armazenado é muito mais complicado do que para uma exibição ou tabela. Um dos problemas é que um procedimento armazenado pode ter vários caminhos de código diferentes dependendo dos parâmetros de entrada e até mesmo coisas que você não pode controlar, como estado do servidor, hora do dia, etc. Por exemplo, o que você esperaria ver como a saída para este procedimento armazenado? E se houver vários conjuntos de resultados, independentemente das condicionais?
CREATE PROCEDURE dbo.foo
@bar INT
AS
BEGIN
SET NOCOUNT ON;
IF @bar = 1
SELECT a, b, c FROM dbo.blat;
ELSE
SELECT d, e, f, g, h FROM dbo.splunge;
END
GO
Se seu procedimento armazenado não tiver caminhos de código e você tiver certeza de que sempre verá o mesmo conjunto de resultados (e pode determinar antecipadamente quais valores devem ser fornecidos se um procedimento armazenado tiver parâmetros não opcionais), vamos dar um exemplo simples:
CREATE PROCEDURE dbo.bar
AS
BEGIN
SET NOCOUNT ON;
SELECT a = 'a', b = 1, c = GETDATE();
END
GO
FMTONLY
Uma maneira é fazer algo assim:
SET FMTONLY ON;
GO
EXEC dbo.bar;
Isso fornecerá um conjunto de resultados vazio e seu aplicativo cliente poderá examinar as propriedades desse conjunto de resultados para determinar os nomes das colunas e os tipos de dados.
Agora, há muitos problemas com
SET FMTONLY ON;
que não entrarei aqui, mas pelo menos deve-se notar que este comando está obsoleto - por um bom motivo. Tenha também o cuidado de SET FMTONLY OFF;
quando terminar, ou você se perguntará por que criou um procedimento armazenado com sucesso, mas não pode executá-lo. E não, não estou avisando sobre isso porque aconteceu comigo. Honesto. :-) OPENQUERY
Ao criar um servidor vinculado de loopback, você pode usar ferramentas como
OPENQUERY
para executar um procedimento armazenado, mas retornar um conjunto de resultados combinável (bem, por favor, aceite isso como uma definição extremamente flexível) que você pode inspecionar. Primeiro crie um servidor de loopback (isso pressupõe uma instância local chamada FOO
):USE master;
GO
EXEC sp_addlinkedserver @server = N'.\FOO', @srvproduct=N'SQL Server'
GO
EXEC sp_serveroption @server=N'.\FOO', @optname=N'data access',
@optvalue=N'true';
Agora podemos pegar o procedimento acima e alimentá-lo em uma consulta como esta:
SELECT * INTO #t
FROM OPENQUERY([.\FOO], 'EXEC dbname.dbo.bar;')
WHERE 1 = 0;
SELECT c.name, t.name
FROM tempdb.sys.columns AS c
INNER JOIN sys.types AS t
ON c.system_type_id = t.system_type_id
WHERE c.[object_id] = OBJECT_ID('tempdb..#t');
Isso ignora tipos de alias (anteriormente conhecidos como tipos de dados definidos pelo usuário) e também pode mostrar duas linhas para colunas definidas como, por exemplo,
sysname
. Mas a partir do acima, produz:name name
---- --------
b int
c datetime
a varchar
Obviamente, há mais trabalho a fazer aqui -
varchar
não mostra comprimento, e você terá que obter precisão/escala para outros tipos, como datetime2
, time
e decimal
. Mas isso é um começo. SQL Server 2012
Existem algumas novas funções no SQL Server 2012 que facilitam muito a descoberta de metadados. Para o procedimento acima, podemos fazer o seguinte:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(
OBJECT_ID('dbo.bar'),
NULL
);
Entre outras coisas, isso realmente fornece precisão e escala e resolve tipos de alias para nós. Para o procedimento acima, isso produz:
name system_type_name
---- ----------------
a varchar(1)
b int
c datetime
Não há muita diferença visualmente, mas quando você começar a entrar em todos os diferentes tipos de dados com várias precisão e escala, você apreciará o trabalho extra que essa função faz por você.
A desvantagem:no SQL Server 2012, pelo menos, essas funções funcionam apenas para o primeiro conjunto de resultados (como o nome da função indica).