Qual é a maneira mais fácil de criar uma tabela temporária no SQL Server que possa conter o resultado de um procedimento armazenado?

50

Muitas vezes, preciso escrever algo como o seguinte ao lidar com o SQL Server.

create table #table_name
(
    column1 int,
    column2 varchar(200)
    ...
)

insert into #table_name
execute some_stored_procedure;

Mas criar uma tabela que tenha a sintaxe exata como resultado de um procedimento armazenado é uma tarefa tediosa. Por exemplo, o resultado de sp_helppublication possui 48 colunas! Quero saber se existe alguma maneira fácil de fazer isso.

Obrigado.

Apenas um aprendiz
fonte
Se você deseja um formato de saída definido (e se você é um DBA, deve preferir um formato definido!), Considere um UDF com valor de tabela. Infelizmente, eles têm algumas limitações sérias, portanto nem sempre são uma opção.
Jon de todos os comércios

Respostas:

36

Se o procedimento retornar apenas um conjunto de resultados e a opção de consultas distribuídas ad hoc estiver ativada.

SELECT * 
INTO #T 
FROM OPENROWSET('SQLNCLI', 
                'Server=(local)\MSSQL2008;Trusted_Connection=yes;',
                 'SET FMTONLY OFF;EXEC sp_who')

Ou você pode configurar um servidor vinculado a loopback e usá-lo.

EXEC sp_addlinkedserver @server = 'LOCALSERVER',  @srvproduct = '',
                        @provider = 'SQLNCLI', @datasrc = @@servername

SELECT *
INTO  #T
FROM OPENQUERY(LOCALSERVER, 
               'SET FMTONLY OFF;
               EXEC sp_who')
Martin Smith
fonte
Você não quer dizer SET FMT_ONLY ON?
Andreas Ågren
@ Andreas - Não, porque eu assumi que a idéia era criar e preencher a tabela a partir da saída do procedimento armazenado.
Martin Smith
18

No SQL Server 2012 e acima, você pode usar sys.dm_exec_describe_first_result_setlocalmente, supondo que o conjunto de resultados que você procura seja o primeiro resultado:

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += ',' + CHAR(13) + CHAR(10) + CHAR(9)
    + name + ' ' + system_type_name
    FROM sys.dm_exec_describe_first_result_set('sp_who', NULL, 1);

SELECT @sql = N'CREATE TABLE #f
(' + STUFF(@sql, 1, 1, N'') + '
);';

PRINT @sql;

Resultado:

CREATE TABLE #f
(
    spid smallint,
    ecid smallint,
    status nchar(30),
    loginame nvarchar(128),
    hostname nchar(128),
    blk char(5),
    dbname nvarchar(128),
    cmd nchar(16),
    request_id int
);

Observe que há uma limitação: se o procedimento armazenado criar #temp tables, a funcionalidade de metadados não funcionará. É por isso que eu não usei sp_who2. :-)

Aaron Bertrand
fonte
A SELECT @sql += *expression*sintaxe está documentada em algum lugar? deve ORDER BYser incluído para torná-lo estável?
Ross Presser
11
@ Ross Sim, foi introduzido no SQL Server 2008 e está documentado aqui . ORDER BYé realmente conhecido por tornar isso menos estável . Se você deseja obter os resultados em uma ordem previsível, use FOR XML PATHou, se houver uma versão suficientemente nova do SQL Server STRING_AGG,.
Aaron Bertrand
11
Correção leve: você vinculou à aritmética +=... string +=está documentada aqui . Mas obrigada!
Ross Presser
@ Ross sim, desculpe.
Aaron Bertrand
3

Não. O resultado de um procedimento armazenado pode variar bastante: ele não foi projetado para sempre retornar exatamente um conjunto de resultados como um SELECT em algum objeto.

Você precisa executar CREATE TABLE

gbn
fonte
1

Eu escreveria um procedimento para gerar a tabela para mim:

CREATE PROCEDURE [dbo].[p_create_table_from_procedure]
    @TABLE_NAME AS NVARCHAR(MAX),
    @PROCEDURE_NAME AS NVARCHAR(MAX)

As
    DECLARE @CREATE_TABLE_QUERY NVARCHAR(MAX) = N'';


    SELECT 
        @CREATE_TABLE_QUERY += ', ' + name + ' ' + UPPER(system_type_name) + CHAR(13) + CHAR(10) + CHAR(9)

    FROM 
        sys.dm_exec_describe_first_result_set(@procedure_name, NULL, 1);


    SELECT 
        @CREATE_TABLE_QUERY = N'CREATE TABLE ' + @table_name + '(' + CHAR(13) + CHAR(10) + CHAR(9) + STUFF(@CREATE_TABLE_QUERY, 1, 1, N'') + ');';

    PRINT @CREATE_TABLE_QUERY;

Em seguida, chame-o com:

EXEC p_create_table_from_procedure 'YOUR_TABLE_NAME_HERE', 'YOUR_PROCEDURE_NAME_HERE'

Nota : Substitua 'YOUR_PROCEDURE_NAME_HERE' pelo nome do seu próprio procedimento armazenado.

Nota : Substitua YOUR_TABLE_NAME_HERE pelo nome da tabela de sua escolha.

O acima irá gerar algo como isto:

CREATE TABLE YOUR_TABLE_NAME_HERE(
     WeekName VARCHAR(40)
    , Line Name VARCHAR(50)
    , TheDate DATETIME
    , ReceivedAll INT
    , Answered INT
    , Abandoned INT
    , Call Length INT
    , WaitTimeAnswer INT
    , WaitTimeAbandon INT
    , PeriodName VARCHAR(10)
    , Week SMALLINT
    , Period SMALLINT
    , Year SMALLINT
    , WeekInPeriod SMALLINT
    , NumWeeksInPeriod SMALLINT
    , WeekendDate DATETIME
    , CRCOperative VARCHAR(100)
    , CallType VARCHAR(20)
    , Charge Time INT
    , SourceNumber VARCHAR(80)
    , DestinationNumber VARCHAR(80)
    , CallStart DATETIME
    , Out of Hours VARCHAR(12)
    , IsWorkingDay BIT
    );
Knickerless-Noggins
fonte
Como isso é diferente da resposta de @ AaronBertrand acima?
Max Vernon