Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Recuperar a definição de coluna para o conjunto de resultados do procedimento armazenado


Então, digamos que você tenha um procedimento armazenado em tempdb:
USE tempdb;
GO

CREATE PROCEDURE dbo.my_procedure
AS
BEGIN
    SET NOCOUNT ON;

    SELECT foo = 1, bar = 'tooth';
END
GO

Existe uma maneira bastante complicada de determinar os metadados que o procedimento armazenado produzirá. Existem várias ressalvas, incluindo o procedimento que só pode gerar um único conjunto de resultados e que uma melhor suposição será feita sobre o tipo de dados se não puder ser determinado com precisão. Requer o uso de OPENQUERY e um servidor vinculado de loopback com o 'DATA ACCESS' propriedade definida como verdadeira. Você pode verificar sys.servers para ver se já tem um servidor válido, mas vamos apenas criar um manualmente chamado loopback :
EXEC master..sp_addlinkedserver 
    @server = 'loopback',  
    @srvproduct = '',
    @provider = 'SQLNCLI',
    @datasrc = @@SERVERNAME;

EXEC master..sp_serveroption 
    @server = 'loopback', 
    @optname = 'DATA ACCESS',
    @optvalue = 'TRUE';

Agora que você pode consultar isso como um servidor vinculado, você pode usar o resultado de qualquer consulta (incluindo uma chamada de procedimento armazenado) como um SELECT normal . Então você pode fazer isso (observe que o prefixo do banco de dados é importante, caso contrário, você receberá o erro 11529 e 2812):
SELECT * FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');

Se pudermos realizar um SELECT * , também podemos executar um SELECT * INTO :
SELECT * INTO #tmp FROM OPENQUERY(loopback, 'EXEC tempdb.dbo.my_procedure;');

E uma vez que a tabela #tmp exista, podemos determinar os metadados dizendo (assumindo o SQL Server 2005 ou superior):
SELECT c.name, [type] = t.name, c.max_length, c.[precision], c.scale
  FROM sys.columns AS c
  INNER JOIN sys.types AS t
  ON c.system_type_id = t.system_type_id
  AND c.user_type_id = t.user_type_id
  WHERE c.[object_id] = OBJECT_ID('tempdb..#tmp');

(Se você estiver usando o SQL Server 2000, poderá fazer algo semelhante com syscolumns, mas não tenho uma instância 2000 à mão para validar uma consulta equivalente.)

Resultados:
name      type    max_length precision scale
--------- ------- ---------- --------- -----
foo       int              4        10     0
bar       varchar          5         0     0

No Denali, isso será muito, muito, muito mais fácil. Novamente, ainda há uma limitação do primeiro conjunto de resultados, mas você não precisa configurar um servidor vinculado e passar por todos esses aros. Você pode apenas dizer:
DECLARE @sql NVARCHAR(MAX) = N'EXEC tempdb.dbo.my_procedure;';

SELECT name, system_type_name
    FROM sys.dm_exec_describe_first_result_set(@sql, NULL, 1);

Resultados:
name      system_type_name
--------- ----------------
foo       int             
bar       varchar(5)      

Até o Denali, sugiro que seria mais fácil apenas arregaçar as mangas e descobrir os tipos de dados por conta própria. Não apenas porque é tedioso seguir as etapas acima, mas também porque é muito mais provável que você faça uma estimativa correta (ou pelo menos mais precisa) do que o mecanismo, já que o tipo de dados que o mecanismo faz será baseado no tempo de execução saída, sem qualquer conhecimento externo do domínio de valores possíveis. Esse fator também permanecerá verdadeiro no Denali, portanto, não fique com a impressão de que os novos recursos de descoberta de metadados são tudo, eles apenas tornam o acima um pouco menos tedioso.

Ah, e para algumas outras pegadinhas em potencial com OPENQUERY , veja o artigo de Erland Sommarskog aqui:

http://www.sommarskog.se/share_data.html#OPENQUERY