¿Cuál es la forma más fácil de crear una tabla temporal en SQL Server que pueda contener el resultado de un procedimiento almacenado?

50

Muchas veces necesito escribir algo como lo siguiente cuando trato con SQL Server.

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

insert into #table_name
execute some_stored_procedure;

Pero crear una tabla que tenga la sintaxis exacta como resultado de un procedimiento almacenado es una tarea tediosa. Por ejemplo, ¡el resultado de sp_helppublication tiene 48 columnas! Quiero saber si hay alguna manera fácil de hacer esto.

Gracias.

Solo un alumno
fuente
Si desea un formato de salida definido (y si es un DBA, ¡debería preferir un formato definido!), Considere un UDF de valor de tabla. Desafortunadamente tienen algunas limitaciones serias, por lo que no siempre son una opción.
Jon of All Trades

Respuestas:

36

Si el procedimiento solo devuelve un conjunto de resultados y la opción de consultas distribuidas ad hoc está habilitada.

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

O puede configurar un servidor vinculado de bucle invertido y usarlo en su lugar.

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

SELECT *
INTO  #T
FROM OPENQUERY(LOCALSERVER, 
               'SET FMTONLY OFF;
               EXEC sp_who')
Martin Smith
fuente
No quiere decir SET FMT_ONLY ON?
Andreas Ågren
@Andreas: no, porque supuse que la idea era crear y completar la tabla a partir de la salida del procedimiento almacenado.
Martin Smith
18

En SQL Server 2012 y versiones posteriores, puede usarlo sys.dm_exec_describe_first_result_setlocalmente, suponiendo que el conjunto de resultados que busca es el primer 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
);

Tenga en cuenta que hay una limitación: si su procedimiento almacenado crea tablas #temp, la funcionalidad de metadatos no funciona. Es por eso que no usé sp_who2. :-)

Aaron Bertrand
fuente
¿La SELECT @sql += *expression*sintaxis está documentada en alguna parte? ¿Debe ORDER BYincluirse un para que sea estable?
Ross Presser
1
@Ross sí, se introdujo en SQL Server 2008 y se documenta aquí . ORDER BYse sabe que hace esto menos estable . Si desea que los resultados en un orden predecible, utilizar FOR XML PATHo, si en una nueva versión bastante de SQL Server, STRING_AGG.
Aaron Bertrand
1
Ligera corrección: se vinculó a la aritmética +=... la cadena +=se documenta aquí . ¡Pero gracias!
Ross Presser
@Ross, lo siento.
Aaron Bertrand
3

No. El resultado de un procedimiento almacenado puede variar enormemente: no está diseñado para devolver siempre exactamente un conjunto de resultados como un SELECT en algún objeto.

Tienes que ejecutar CREATE TABLE

gbn
fuente
1

Escribiría un procedimiento para generar la tabla para mí:

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;

Luego llámalo con:

EXEC p_create_table_from_procedure 'YOUR_TABLE_NAME_HERE', 'YOUR_PROCEDURE_NAME_HERE'

Nota : Reemplace 'YOUR_PROCEDURE_NAME_HERE' con el nombre de su propio procedimiento almacenado.

Nota : Reemplace YOUR_TABLE_NAME_HERE con el nombre de la tabla que elija.

Lo anterior generará algo como esto:

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
fuente
¿Cómo es esto diferente de la respuesta de @ AaronBertrand anterior?
Max Vernon