Insertar resultados de un procedimiento almacenado en una tabla temporal

1579

¿Cómo hago un SELECT * INTO [temp table] FROM [stored procedure]? ¿No FROM [Table]y sin definir [temp table]?

SelectTodos los datos de BusinessLineen tmpBusLinefunciona bien.

select *
into tmpBusLine
from BusinessLine

Estoy intentando lo mismo, pero usar un stored procedureque devuelve datos no es exactamente lo mismo.

select *
into tmpBusLine
from
exec getBusinessLineHistory '16 Mar 2009'

Mensaje de salida:

Mensaje 156, Nivel 15, Estado 1, Línea 2 Sintaxis incorrecta cerca de la palabra clave 'exec'.

He leído varios ejemplos de creación de una tabla temporal con la misma estructura que el procedimiento almacenado de salida, que funciona bien, pero sería bueno no proporcionar ninguna columna.

Ferdeen
fuente
22
Con SELECT * INTO [TABLE NAME] usted conoce las columnas, ya que se copian de la tabla original. Esto es exactamente lo que quiero si tuviera que hacer lo mismo contra un procedimiento almacenado.
Ferdeen
77
Solo quiero señalar que "select * into tmpBusLine" crea una tabla permanente. Probablemente desee "seleccionar * en #tmpBusLine". Estoy seguro de que el póster original ya lo descubrió, pero podría ayudar a otros que encuentren esta publicación, ya que es el principal resultado actualmente para la búsqueda "seleccionar en la tabla temporal"
ktam33
2
No sé si esto se ha abordado o no, pero la razón por la que obtiene el error es por la palabra clave from.
Wes Palmer
99
¡Microsoft necesita agregar SELECT * INTO FROM EXEC! ¡Por favor!
kjmerf

Respuestas:

704

Puede usar OPENROWSET para esto. Echar un vistazo. También he incluido el código sp_configure para habilitar consultas distribuidas ad hoc, en caso de que no esté habilitado.

CREATE PROC getBusinessLineHistory
AS
BEGIN
    SELECT * FROM sys.databases
END
GO

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
     'EXEC getBusinessLineHistory')

SELECT * FROM #MyTempTable
Aaron Alton
fuente
28
Esta es la forma correcta de hacerlo. OPENROWSET es prácticamente la única forma de tratar los resultados de un procedimiento almacenado como una expresión de tabla.
Rob Farley
37
Esto parece un poco engorroso solo para insertarlo en una tabla. Mucha configuración para hacer. Además, cuando lo probé, obtuve "Mensaje 7357, Nivel 16, Estado 2, Línea 1. No se puede procesar el objeto" EXEC GetPartyAnalysisData 146 ". El proveedor OLE DB" SQLNCLI "para el servidor vinculado" (nulo) "indica que el objeto tiene sin columnas o el usuario actual no tiene permisos sobre ese objeto ". Entonces necesita configurar un servidor vinculado ...
Ferdeen
10
No necesita un servidor vinculado, pero necesitaría obtener la cadena de conexión correcta ... y también, especificar la ruta completa al procedimiento almacenado, incluido el nombre de la base de datos y el propietario del sp.
MartW
18
eeeeew! una referencia al mismo servidor? asqueroso. definitivamente más hack que tener que crear manualmente la tabla temporal
Tim Abell
23
Estoy de acuerdo en que este es un truco y que probablemente debería evitarse a menos que su espalda esté contra la pared. Cambiar el sp a una función es probablemente un mejor ángulo para tomar. EN MI HUMILDE OPINIÓN.
greg
624

Si desea hacerlo sin declarar primero la tabla temporal, puede intentar crear una función definida por el usuario en lugar de un procedimiento almacenado y hacer que esa función definida por el usuario devuelva una tabla. Alternativamente, si desea utilizar el procedimiento almacenado, intente algo como esto:

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'
Gavin
fuente
171
Creo que el punto era generar el esquema sin tener que declararlo explícitamente.
Craig
55
Me interesaría saber cuál es la diferencia entre esto y la solución de @Aaron Alton anterior. Este parece mucho más simple, pero no estoy seguro de otras implicaciones.
funkymushroom 01 de
11
Esto funcionará, pero si alguna vez agrega columnas adicionales al procedimiento almacenado SpGetRecords, esto explotará.
Brady Holt
15
Solo obtienes un INSERT INTO EXEC por pila de llamadas. SpGetRecords y cualquier otro proceso al que llame no pueden usar esta estrategia en su propio código. Esto puede sorprender a los mantenedores de SpGetRecords.
Matt Stephenson el
33
Esto no responde a la pregunta en absoluto y no veo por qué está tan votado. El OP declaró explícitamente "sin definir [tabla temporal]" y su primera línea tiene una declaración de crear tabla temporal.
NickG
296

En SQL Server 2005 puede usar INSERT INTO ... EXECpara insertar el resultado de un procedimiento almacenado en una tabla. De la INSERTdocumentación de MSDN (para SQL Server 2000, de hecho):

--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales
Matt Hamilton
fuente
122
Esto requiere que author_sales se defina por adelantado. Estoy tratando de evitar esto. Gracias.
Ferdeen
55
Pensé tanto. Muy útil Insertar en tablas tmp sobre la marcha, pero no tan útil si necesita conocer la estructura del conjunto de datos devuelta desde un proceso almacenado. Gracias por tu ayuda.
Ferdeen
3
Hay un buen artículo aquí msdn.microsoft.com/en-us/library/aa175921.aspx
Rich Andrews
44
Para usar el mismo esquema, puede hacer una copia de la siguiente manera: seleccione top 0 * en tempTable de realTable ( stackoverflow.com/a/9206463/73794 )
Even Mien
@EvenMien Me emocioné momentáneamente cuando vi tu comentario ... pero lamentablemente eso solo funciona si los resultados de tu proceso realmente reflejan una tabla real :(
BVernon
193

Esta es una respuesta a una versión ligeramente modificada de su pregunta. Si puede abandonar el uso de un procedimiento almacenado para una función definida por el usuario, puede usar una función definida por el usuario con valores de tabla en línea. Este es esencialmente un procedimiento almacenado (tomará parámetros) que devuelve una tabla como un conjunto de resultados; y por lo tanto se colocará muy bien con una declaración INTO.

Aquí hay un buen artículo rápido sobre él y otras funciones definidas por el usuario. Si aún tiene la necesidad de conducir un procedimiento almacenado, puede ajustar la función definida por el usuario en línea con un procedimiento almacenado. El procedimiento almacenado solo pasa parámetros cuando llama a select * desde la función definida por el usuario en línea con valores de tabla.

Entonces, por ejemplo, tendría una función definida por el usuario con valores de tabla en línea para obtener una lista de clientes para una región en particular:

CREATE FUNCTION CustomersByRegion 
(  
    @RegionID int  
)
RETURNS TABLE 
AS
RETURN 
  SELECT *
  FROM customers
  WHERE RegionID = @RegionID
GO

Luego puede llamar a esta función para obtener cuáles son sus resultados:

SELECT * FROM CustomersbyRegion(1)

O para hacer un SELECCIONAR EN:

SELECT * INTO CustList FROM CustomersbyRegion(1)

Si aún necesita un procedimiento almacenado, ajuste la función como tal:

CREATE PROCEDURE uspCustomersByRegion 
(  
    @regionID int  
)
AS
BEGIN
     SELECT * FROM CustomersbyRegion(@regionID);
END
GO

Creo que este es el método más 'hack-less' para obtener los resultados deseados. Utiliza las funciones existentes, ya que estaban destinadas a ser utilizadas sin complicaciones adicionales. Al anidar la función definida por el usuario con valores de tabla en línea en el procedimiento almacenado, tiene acceso a la funcionalidad de dos maneras. ¡Más! Solo tiene un punto de mantenimiento para el código SQL real.

Se ha sugerido el uso de OPENROWSET, pero esto no es para lo que se pretendía utilizar la función OPENROWSET (From Books Online):

Incluye toda la información de conexión necesaria para acceder a datos remotos desde un origen de datos OLE DB. Este método es una alternativa para acceder a las tablas en un servidor vinculado y es un método ad hoc de una sola vez para conectar y acceder a datos remotos mediante OLE DB. Para referencias más frecuentes a las fuentes de datos OLE DB, use servidores vinculados en su lugar.

El uso de OPENROWSET hará el trabajo, pero incurrirá en una sobrecarga adicional para abrir conexiones locales y reunir datos. También puede no ser una opción en todos los casos, ya que requiere un permiso de consulta ad hoc que plantea un riesgo de seguridad y, por lo tanto, puede no ser deseable. Además, el enfoque OPENROWSET impedirá el uso de procedimientos almacenados que devuelvan más de un conjunto de resultados. Ajustar varias funciones definidas por el usuario en línea con valores de tabla en un solo procedimiento almacenado puede lograr esto.

Christian Loris
fuente
44
+1 Una función con valores de tabla es una solución adecuada. Deberíamos tomar nota de los inconvenientes menores: la función con valores de tabla es un objeto de base de datos adicional, y puede ser necesario otorgarle privilegios.
spencer7593
2
Amo la solución. Un inconveniente menor que golpeé es que mi tabla no puede tener un orden por dónde, ya que podría tenerla en el procedimiento almacenado. Owh bueno, lo resolveré
mrwaim
55
Un inconveniente más: "No se puede acceder a tablas temporales desde una función"
mrwaim
77
La pregunta original es cómo creamos una tabla temporal con los resultados de la sp. Este es un buen patrón, pero no aborda esta pregunta
greg
16
Greg, la primera línea de mi respuesta dice "Esta es una respuesta a una versión ligeramente modificada de su pregunta". Tu comentario es redundante.
Christian Loris
131
EXEC sp_serveroption 'YOURSERVERNAME', 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpTable
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.sproc 1')
Quassnoi
fuente
2
Obtenga un "Mensaje 208, Nivel 16, Estado 1, Línea 1 Nombre de objeto no válido 'tmpBusLine' (probablemente porque no está definido por adelantado).
Ferdeen
1
@Ferds: lo siento, no entendí tu solicitud al principio. Actualizado con otra solución.
Quassnoi el
26
Gran solución Una advertencia: deberá habilitar 'ACCESO A DATOS' en su servidor: EXEC sp_serveroption 'TheServerName', 'ACCESO A DATOS', VERDADERO
jcollum el
8
También deberá permitir el acceso remoto al servidor. Esto tendrá ramificaciones de seguridad.
BraveNewMath
77
Esto no funcionará si el procedimiento almacenado de destino utiliza tablas temporales
Sal
125

La solución más fácil:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

Si no conoce el esquema, puede hacer lo siguiente. Tenga en cuenta que existen graves riesgos de seguridad en este método.

SELECT * 
INTO #temp
FROM OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [db].[schema].[sproc]')
Tigerjz32
fuente
si no conozco la columna del conjunto de resultados devuelto, entonces ??? Me refiero a la columna puede variar. Entonces, ¿cómo insertar el resultado en la tabla temporal?
SHEKHAR SHETE
Puede usar OPENQUERY pero no se recomienda ya que viene con fallas de seguridad.
Tigerjz32
1
"si no conozco la columna del conjunto de resultados devuelto", entonces no puede usarlo en su lógica. ¿Cómo usará los datos si no sabe qué son?
Adriaan Davel
@AdriaanDavel Estoy de acuerdo con usted en que siempre debe conocer sus datos (mejor práctica), sin embargo, lo que podría estar diciendo es que hay momentos en que el sproc devuelve columnas dinámicas y no siempre sabe cómo se verá el esquema. En ese caso, puede usar OPENROWSET para insertar y crear una tabla sobre la marcha. Sin embargo, existen riesgos de seguridad obvios al hacer esto ...
Tigerjz32
1
@nurettin a veces no sabes qué va a devolver el procedimiento almacenado. ¿Qué pasa en ese caso? ¿Cómo podría crear una tabla temporal (cuando no sabe qué devolverá el procedimiento almacenado) e insertarla desde un procedimiento almacenado?
Tigerjz32
106

Cuando el procedimiento almacenado devuelve muchas columnas y no desea "crear" manualmente una tabla temporal para contener el resultado, he descubierto que la forma más fácil es ir al procedimiento almacenado y agregar una cláusula "into" en la última instrucción select y agregue 1 = 0 a la cláusula where.

Ejecute el procedimiento almacenado una vez y regrese y elimine el código SQL que acaba de agregar. Ahora, tendrá una tabla vacía que coincide con el resultado del procedimiento almacenado. Puede "escribir la tabla como crear" para una tabla temporal o simplemente insertarla directamente en esa tabla.

dotjoe
fuente
99
+1, excelente sugerencia. Incluso podría agregar una variable opcional rápida a la sproc llamada @TableCreate o algo similar que cuando no es nulo siga los pasos anteriores. No requiere cambiar el sproc, una vez que está configurado.
Ian Roke el
1
@dotjoe ¿Haces SELECT INTOuna tabla temporal y haces una tabla de script como crear desde la tabla temporal? Las tablas temporales aparecen tempdbpero no puedo hacer un clic derecho y crear un script. Cualquier ayuda es apreciada.
DotnetDude
2
@DotNetDude puede select ... into new_tablecrear implícitamente una tabla real.
dotjoe
Luego tome la definición de columna aproximada del esquema de tabla vacía; reemplace '...' al final con TABLE_NAME legítimo:declare @s varchar(max)='';select @s=@s+','+COLUMN_NAME+' '+DATA_TYPE+isnull('('+case CHARACTER_MAXIMUM_LENGTH when -1 then 'max' else cast(CHARACTER_MAXIMUM_LENGTH as varchar(10))end+')','')from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='...';select @s
user423430
¡Esta es la mejor solución!
Lucas925
66
declare @temp table
(
    name varchar(255),
    field varchar(255),
    filename varchar(255),
    filegroup varchar(255),
    size varchar(255),
    maxsize varchar(255),
    growth varchar(255),
    usage varchar(255)
);
INSERT @temp  Exec sp_helpfile;
select * from @temp;
nitina
fuente
3
no aborda la pregunta original de OP, haciendo la inserción sin definir primero la tabla temporal.
Hacia
48

¿Su procedimiento almacenado solo recupera los datos o los modifica también? Si se usa solo para recuperar, puede convertir el procedimiento almacenado en una función y usar las expresiones de tabla comunes (CTE) sin tener que declararlo, de la siguiente manera:

with temp as (
    select * from dbo.fnFunctionName(10, 20)
)
select col1, col2 from temp

Sin embargo, lo que sea necesario recuperar del CTE debe usarse en una sola declaración. No puede hacer un with temp as ...e intentar usarlo después de un par de líneas de SQL. Puede tener múltiples CTE en una declaración para consultas más complejas.

Por ejemplo,

with temp1020 as (
    select id from dbo.fnFunctionName(10, 20)
),
temp2030 as (
    select id from dbo.fnFunctionName(20, 30)
)
select * from temp1020 
where id not in (select id from temp2030)
Usuario SO
fuente
1
Estas no son tablas temporales, son CTE. technet.microsoft.com/en-us/library/…
yucer
55
Gracias @yucer ... Creo que no sabía que se llamaban CTE en ese entonces :)
SO Usuario
48

Si la tabla de resultados de su proceso almacenado es demasiado complicada para escribir a mano la declaración "crear tabla", y no puede usar OPENQUERY O OPENROWSET, puede usar sp_help para generar la lista de columnas y tipos de datos por usted. Una vez que tenga la lista de columnas, solo es cuestión de formatearla para satisfacer sus necesidades.

Paso 1: Agregue "en #temp" a la consulta de salida (por ejemplo, "seleccione [...] en #temp desde [...]").

La forma más fácil es editar la consulta de salida en el proceso directamente. Si no puede cambiar el proceso almacenado, puede copiar el contenido en una nueva ventana de consulta y modificar la consulta allí.

Paso 2: Ejecute sp_help en la tabla temporal. (por ejemplo, "exec tempdb..sp_help #temp")

Después de crear la tabla temporal, ejecute sp_help en la tabla temporal para obtener una lista de las columnas y los tipos de datos, incluido el tamaño de los campos varchar.

Paso 3: copie las columnas y los tipos de datos en una declaración de creación de tabla

Tengo una hoja de Excel que utilizo para formatear la salida de sp_help en una declaración de "crear tabla". No necesita nada tan elegante, solo copie y pegue en su editor de SQL. Use los nombres, tamaños y tipos de columna para construir una instrucción "Crear tabla #x [...]" o "declarar tabla @x" [...] que puede usar para INSERTAR los resultados del procedimiento almacenado.

Paso 4: insertar en la tabla recién creada

Ahora tendrá una consulta similar a las otras soluciones descritas en este hilo.

DECLARE @t TABLE 
(
   --these columns were copied from sp_help
   COL1 INT,
   COL2 INT   
)

INSERT INTO @t 
Exec spMyProc 

Esta técnica también se puede utilizar para convertir una tabla temporal ( #temp) en una variable de tabla ( @temp). Si bien esto puede ser más pasos que solo escribir la create tabledeclaración usted mismo, evita errores manuales como errores tipográficos y desajustes de tipo de datos en procesos grandes. La depuración de un error tipográfico puede llevar más tiempo que escribir la consulta en primer lugar.

Puño de la furia
fuente
37

Si el OPENROWSET le está causando problemas, hay otra forma a partir de 2012; utilice sys.dm_exec_describe_first_result_set_for_object, como se menciona aquí: ¿ Recuperar nombres de columnas y tipos de un procedimiento almacenado?

Primero, cree este procedimiento almacenado para generar el SQL para la tabla temporal:

CREATE PROCEDURE dbo.usp_GetStoredProcTableDefinition(
    @ProcedureName  nvarchar(128),
    @TableName      nvarchar(128),
    @SQL            nvarchar(max) OUTPUT
)
AS
SET @SQL = 'CREATE TABLE ' + @tableName + ' ('

SELECT @SQL = @SQL + '['+name +'] '+ system_type_name +''  + ','
        FROM sys.dm_exec_describe_first_result_set_for_object
        (
          OBJECT_ID(@ProcedureName), 
          NULL
        );

--Remove trailing comma
SET @SQL = SUBSTRING(@SQL,0,LEN(@SQL))    
SET @SQL =  @SQL +')'

Para usar el procedimiento, llámelo de la siguiente manera:

DECLARE     @SQL    NVARCHAR(MAX)

exec dbo.usp_GetStoredProcTableDefinition
    @ProcedureName='dbo.usp_YourProcedure',
    @TableName='##YourGlobalTempTable',@SQL = @SQL OUTPUT

INSERT INTO ##YourGlobalTempTable
EXEC    [dbo].usp_YourProcedure

select * from ##YourGlobalTempTable

Tenga en cuenta que estoy usando una tabla temporal global. Esto se debe a que el uso de EXEC para ejecutar el SQL dinámico crea su propia sesión, por lo que una tabla temporal ordinaria estaría fuera del alcance de cualquier código posterior. Si una tabla temporal global es un problema, puede usar una tabla temporal ordinaria, pero cualquier SQL posterior debería ser dinámico, es decir, también ejecutado por la instrucción EXEC.

StuartQ
fuente
44
Olvidaste crear la tabla desde @SQL.
Trisped
32

Quassnoi me puso casi todo el camino, pero faltaba una cosa:

**** Necesitaba usar parámetros en el procedimiento almacenado. ****

Y OPENQUERY no permite que esto suceda:

¡Así que encontré una forma de trabajar el sistema y no tuve que hacer que la definición de la tabla fuera tan rígida, y redefinirla dentro de otro procedimiento almacenado (y, por supuesto, correr el riesgo de que se rompa)!

Sí, puede crear dinámicamente la definición de tabla devuelta por el procedimiento almacenado mediante el uso de la instrucción OPENQUERY con varaiables falsos (siempre y cuando NO RESULT SET devuelva el mismo número de campos y en la misma posición que un conjunto de datos con buenos datos).

Una vez que se crea la tabla, puede utilizar el procedimiento almacenado exec en la tabla temporal durante todo el día.


Y para tener en cuenta (como se indicó anteriormente) debe habilitar el acceso a datos,

EXEC sp_serveroption 'MYSERVERNAME', 'DATA ACCESS', TRUE

Código:

declare @locCompanyId varchar(8)
declare @locDateOne datetime
declare @locDateTwo datetime

set @locDateOne = '2/11/2010'
set @locDateTwo = getdate()

--Build temporary table (based on bogus variable values)
--because we just want the table definition and
--since openquery does not allow variable definitions...
--I am going to use bogus variables to get the table defintion.

select * into #tempCoAttendanceRpt20100211
FROM OPENQUERY(DBASESERVER,
  'EXEC DATABASE.dbo.Proc_MyStoredProc 1,"2/1/2010","2/15/2010 3:00 pm"')

set @locCompanyId = '7753231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

set @locCompanyId = '9872231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

select * from #tempCoAttendanceRpt20100211
drop table #tempCoAttendanceRpt20100211

Gracias por la información que se proporcionó originalmente ... Sí, finalmente no tengo que crear todas estas definiciones de tabla falsas (estrictas) cuando utilizo datos de otro procedimiento almacenado o base de datos, y sí, también puede usar parámetros.

Buscar etiquetas de referencia:

  • Procedimiento almacenado de SQL 2005 en la tabla temporal

  • consulta abierta con procedimiento almacenado y variables 2005

  • consulta abierta con variables

  • ejecutar el procedimiento almacenado en la tabla temporal

Actualización: esto no funcionará con tablas temporales, así que tuve que recurrir a crear manualmente la tabla temporal.

Aviso de Bummer : esto no funcionará con tablas temporales , http://www.sommarskog.se/share_data.html#OPENQUERY

Referencia: Lo siguiente es definir LOCALSERVER. Puede parecer una palabra clave en el ejemplo, pero de hecho es solo un nombre. Así es como lo haces:

sp_addlinkedserver @server = 'LOCALSERVER',  @srvproduct = '',
                   @provider = 'SQLOLEDB', @datasrc = @@servername

Para crear un servidor vinculado, debe tener el permiso ALTERAR CUALQUIER SERVIDOR, o ser miembro de cualquiera de los roles fijos de servidor sysadmin o setupadmin.

OPENQUERY abre una nueva conexión a SQL Server. Esto tiene algunas implicaciones:

El procedimiento que llama con OPENQUERY no puede hacer referencia a tablas temporales creadas en la conexión actual.

La nueva conexión tiene su propia base de datos predeterminada (definida con sp_addlinkedserver, la predeterminada es maestra), por lo que toda especificación de objeto debe incluir un nombre de base de datos.

Si tiene una transacción abierta y mantiene bloqueos cuando llama a OPENQUERY, el procedimiento llamado no puede acceder a lo que bloquea. Es decir, si no tienes cuidado, te bloquearás.

La conexión no es gratuita, por lo que hay una penalización de rendimiento.

Doug Lubey de Louisiana
fuente
1
Si no conoce el nombre de su servidor, use SELECT @@SERVERNAME. También puede usarEXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
Contango
24

Si tiene la suerte de tener SQL 2012 o superior, puede usar dm_exec_describe_first_result_set_for_object

Acabo de editar el sql proporcionado por gotqn. Gracias gotqn.

Esto crea una tabla temporal global con el mismo nombre que el nombre del procedimiento. La tabla temporal se puede usar más tarde según sea necesario. Simplemente no olvides soltarlo antes de volver a ejecutarlo.

    declare @procname nvarchar(255) = 'myProcedure',
            @sql nvarchar(max) 

    set @sql = 'create table ##' + @procname + ' ('
    begin
            select      @sql = @sql + '[' + r.name + '] ' +  r.system_type_name + ','
            from        sys.procedures AS p
            cross apply sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r
            where       p.name = @procname

            set @sql = substring(@sql,1,len(@sql)-1) + ')'
            execute (@sql)
            execute('insert ##' + @procname + ' exec ' + @procname)
    end
Sandeep Gaadhe
fuente
1
¡Excelente! Solo un comentario: use en sys.all_objectslugar de sys.proceduressi desea hacer esto para los procedimientos almacenados incorporados.
Gert Arnold
2
Esto también fallará si el SP usa tablas temporales dentro de él. (pero es bastante útil tener esto como un proceso en tu arsenal)
Trubs
23

Este proceso almacenado hace el trabajo:

CREATE PROCEDURE [dbo].[ExecIntoTable]
(
    @tableName          NVARCHAR(256),
    @storedProcWithParameters   NVARCHAR(MAX)
)
AS
BEGIN
    DECLARE @driver         VARCHAR(10)
    DECLARE @connectionString   NVARCHAR(600)
    DECLARE @sql            NVARCHAR(MAX)
    DECLARE @rowsetSql      NVARCHAR(MAX)

    SET @driver = '''SQLNCLI'''

    SET @connectionString = 
        '''server=' + 
            CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(256)) + 
            COALESCE('\' + CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(256)), '') + 
        ';trusted_connection=yes'''

    SET @rowsetSql = '''EXEC ' + REPLACE(@storedProcWithParameters, '''', '''''') + ''''

    SET @sql = '
SELECT
    *
INTO 
    ' + @tableName + ' 
FROM
    OPENROWSET(' + @driver + ',' + @connectionString + ',' + @rowsetSql + ')'

    EXEC (@sql)
END
GO

Es una pequeña modificación de esto: inserte los resultados del procedimiento almacenado en la tabla para que realmente funcione.

Si desea que funcione con una tabla temporal, deberá usar una ##GLOBALtabla y soltarla después.

satnhak
fuente
17

Para insertar el primer conjunto de registros de un procedimiento almacenado en una tabla temporal, necesita saber lo siguiente:

  1. solo el primer conjunto de filas del procedimiento almacenado se puede insertar en una tabla temporal
  2. el procedimiento almacenado no debe ejecutar la declaración dinámica de T-SQL ( sp_executesql)
  3. primero debe definir la estructura de la tabla temporal

Lo anterior puede parecer una limitación, pero en mi humilde opinión, tiene mucho sentido: si está utilizando sp_executesql, puede devolver dos columnas y una vez diez, y si tiene múltiples conjuntos de resultados, no puede insertarlos en varias tablas también: puede insertar el máximo en dos tablas en una declaración T-SQL (usando OUTPUTcláusula y sin desencadenantes).

Entonces, el problema es principalmente cómo definir la estructura de la tabla temporal antes de realizar la EXEC ... INTO ...declaración.

El primero funciona OBJECT_IDmientras que el segundo y el tercero también funcionan con consultas ad-hoc. Prefiero usar el DMV en lugar del sp, ya que puedes usar CROSS APPLYy construir las definiciones de tabla temporales para múltiples procedimientos al mismo tiempo.

SELECT p.name, r.* 
FROM sys.procedures AS p
CROSS APPLY sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r;

Además, preste atención al system_type_namecampo, ya que puede ser muy útil. Almacena la definición completa de la columna. Por ejemplo:

smalldatetime
nvarchar(max)
uniqueidentifier
nvarchar(1000)
real
smalldatetime
decimal(18,2)

y puede usarlo directamente en la mayoría de los casos para crear la definición de la tabla.

Por lo tanto, creo que en la mayoría de los casos (si el procedimiento almacenado coincide con ciertos criterios), puede crear fácilmente declaraciones dinámicas para resolver tales problemas (cree la tabla temporal, inserte el resultado del procedimiento almacenado, haga lo que necesite con los datos) .


Tenga en cuenta que los objetos anteriores no pueden definir los primeros datos del conjunto de resultados en algunos casos, como cuando se ejecutan sentencias T-SQL dinámicas o se usan tablas temporales en el procedimiento almacenado.

gotqn
fuente
observación práctica sobre limitaciones: si tiene que insertar la salida de algunos sp (llamémosle SP_LEVEL_0) a la tabla temporal creada dinámicamente usando el enfoque anterior en otro sp (llamémosle SP_LEVEL_1), no puede hacer el mismo truco para la salida de este SP_LEVEL_1 a alguna otra tabla temporal en SP_LEVEL_2
nahab
17
  1. Estoy creando una tabla con el siguiente esquema y datos.
  2. Crea un procedimiento almacenado.
  3. Ahora sé cuál es el resultado de mi procedimiento, por lo que estoy realizando la siguiente consulta.

    CREATE TABLE [dbo].[tblTestingTree](
        [Id] [int] IDENTITY(1,1) NOT NULL,
        [ParentId] [int] NULL,
        [IsLeft] [bit] NULL,
        [IsRight] [bit] NULL,
    CONSTRAINT [PK_tblTestingTree] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    SET IDENTITY_INSERT [dbo].[tblTestingTree] ON
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (1, NULL, NULL, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (2, 1, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (3, 1, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (4, 2, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (5, 2, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (6, 3, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (7, 3, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (8, 4, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (9, 4, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (10, 5, 1, NULL)
    
    SET IDENTITY_INSERT [dbo].[tblTestingTree] OFF

    VALUES (10, 5, 1, NULL) SET IDENTITY_INSERT [dbo]. [TblTestingTree] Activado

    create procedure GetDate
    as
    begin
        select Id,ParentId from tblTestingTree
    end
    
    create table tbltemp
    (
        id int,
        ParentId int
    )
    insert into tbltemp
    exec GetDate
    
    select * from tbltemp;
Devansh
fuente
15

Si la consulta no contiene parámetro, use OpenQueryelse use OpenRowset.

Lo básico sería crear un esquema según el procedimiento almacenado e insertarlo en esa tabla. p.ej:

DECLARE @abc TABLE(
                  RequisitionTypeSourceTypeID INT
                , RequisitionTypeID INT
                , RequisitionSourcingTypeID INT
                , AutoDistOverride INT
                , AllowManagerToWithdrawDistributedReq INT
                , ResumeRequired INT
                , WarnSupplierOnDNRReqSubmission  INT
                , MSPApprovalReqd INT
                , EnableMSPSupplierCounterOffer INT
                , RequireVendorToAcceptOffer INT
                , UseCertification INT
                , UseCompetency INT
                , RequireRequisitionTemplate INT
                , CreatedByID INT
                , CreatedDate DATE
                , ModifiedByID INT
                , ModifiedDate DATE
                , UseCandidateScheduledHours INT
                , WeekEndingDayOfWeekID INT
                , AllowAutoEnroll INT
                )
INSERT INTO @abc
EXEC [dbo].[usp_MySp] 726,3
SELECT * FROM @abc
Solucionador de problemas
fuente
13

Código

CREATE TABLE #T1
(
    col1 INT NOT NULL,
    col2 NCHAR(50) NOT NULL,
    col3 TEXT NOT NULL,
    col4 DATETIME NULL,
    col5 NCHAR(50) NULL,
    col6 CHAR(2) NULL,
    col6 NCHAR(100) NULL,
    col7 INT NULL,
    col8 NCHAR(50) NULL,
    col9 DATETIME NULL,
    col10 DATETIME NULL
)

DECLARE @Para1 int
DECLARE @Para2 varchar(32)
DECLARE @Para3 varchar(100)
DECLARE @Para4 varchar(15)
DECLARE @Para5 varchar (12)
DECLARE @Para6 varchar(1)
DECLARE @Para7 varchar(1)


SET @Para1 = 1025
SET @Para2 = N'6as54fsd56f46sd4f65sd'
SET @Para3 = N'XXXX\UserName'
SET @Para4 = N'127.0.0.1'
SET @Para5 = N'XXXXXXX'
SET @Para6 = N'X'
SET @Para7 = N'X'

INSERT INTO #T1
(
    col1,
    col2,
    col3,
    col4,
    col5,
    col6,
    col6,
    col7,
    col8,
    col9,
    col10,
)
EXEC [dbo].[usp_ProcedureName] @Para1, @Para2, @Para3, @Para4, @Para5, @Para6, @Para6

Espero que esto ayude. Por favor califique según corresponda.

SoftwareARM
fuente
11

Encontré Pasar matrices / tablas de datos en procedimientos almacenados que podrían darle otra idea sobre cómo resolver su problema.

El enlace sugiere utilizar un parámetro de tipo de imagen para pasar al procedimiento almacenado. Luego, en el procedimiento almacenado, la imagen se transforma en una variable de tabla que contiene los datos originales.

Tal vez hay una manera de que esto se pueda usar con una tabla temporal.

kevchadders
fuente
44
Esto ya no es necesario en las versiones Sql2008 y posteriores con la introducción de los parámetros de valor de tabla . Ahora puede pasar directamente un conjunto de datos .net o un objeto de tabla de datos a un procedimiento almacenado sql con la conversión a byte como se menciona en el enlace anterior
EndlessSpace
10

Encontré el mismo problema y esto es lo que hice para esto a partir de la sugerencia de Paul . La parte principal es usar NEWID()para evitar que varios usuarios ejecuten los procedimientos / scripts de la tienda al mismo tiempo, lo que es una molestia para la tabla temporal global.

DECLARE @sql varchar(max) = '', 
@tmp_global_table varchar(255) = '##global_tmp_' + CONVERT(varchar(36), NEWID())
SET @sql = @sql + 'select * into [' + @tmp_global_table + '] from YOURTABLE'
EXEC(@sql)

EXEC('SELECT * FROM [' + @tmp_global_table + ']')
zhongxiao37
fuente
9

Otro método es crear un tipo y usar PIPELINED para luego devolver su objeto. Sin embargo, esto se limita a conocer las columnas. Pero tiene la ventaja de poder hacer:

SELECT * 
FROM TABLE(CAST(f$my_functions('8028767') AS my_tab_type))
pierre
fuente
¿Que es esto? No parece tener nada que ver con SQL Server sobre el que trata esta pregunta
Martin Smith
8

Es un proceso simple de 2 pasos: - crear una tabla temporal - Insertar en la tabla temporal.

Código para realizar lo mismo:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;
S Krishna
fuente
Votado a favor; muy similar a las respuestas existentes.
iokevins
6

Después de buscar, encontré una manera de crear una tabla temporal dinámicamente para cualquier procedimiento almacenado sin usar OPENROWSETo OPENQUERYusar un esquema genérico de la definición de resultados del Procedimiento almacenado, especialmente cuando no es administrador de la base de datos.

El servidor SQL tiene un proceso integrado sp_describe_first_result_setque puede proporcionarle un esquema de cualquier conjunto de resultados de procedimientos. Creé una tabla de esquema a partir de los resultados de este procedimiento y configuré manualmente todo el campo en NULLABLE.

declare @procname varchar(100) = 'PROCEDURENAME' -- your procedure name
declare @param varchar(max) = '''2019-06-06''' -- your parameters 
declare @execstr nvarchar(max) = N'exec ' + @procname
declare @qry nvarchar(max)

-- Schema table to store the result from sp_describe_first_result_set.
create table #d
(is_hidden  bit  NULL, column_ordinal   int  NULL, name sysname NULL, is_nullable   bit  NULL, system_type_id   int  NULL, system_type_name nvarchar(256) NULL,
max_length  smallint  NULL, precision   tinyint  NULL,  scale   tinyint  NULL,  collation_name  sysname NULL, user_type_id  int NULL, user_type_database    sysname NULL,
user_type_schema    sysname NULL,user_type_name sysname NULL,assembly_qualified_type_name   nvarchar(4000),xml_collection_id    int NULL,xml_collection_database    sysname NULL,
xml_collection_schema   sysname NULL,xml_collection_name    sysname NULL,is_xml_document    bit  NULL,is_case_sensitive bit  NULL,is_fixed_length_clr_type  bit  NULL,
source_server   sysname NULL,source_database    sysname NULL,source_schema  sysname NULL,source_table   sysname NULL,source_column  sysname NULL,is_identity_column bit NULL,
is_part_of_unique_key   bit NULL,is_updateable  bit NULL,is_computed_column bit NULL,is_sparse_column_set   bit NULL,ordinal_in_order_by_list   smallint NULL,
order_by_list_length    smallint NULL,order_by_is_descending    smallint NULL,tds_type_id   int  NULL,tds_length    int  NULL,tds_collation_id  int NULL,
tds_collation_sort_id   tinyint NULL)


-- Get result set definition of your procedure
insert into #d
EXEC sp_describe_first_result_set @exestr, NULL, 0

-- Create a query to generate and populate a global temp table from above results
select 
@qry = 'Create table ##t(' +
stuff(  
    (select ',' + name + ' '+ system_type_name + ' NULL'
    from #d d For XML Path, TYPE)
    .value(N'.[1]', N'nvarchar(max)')
, 1,1,'')
+ ')

insert into ##t 
Exec '+@procname+' ' + @param

Exec sp_executesql @qry

-- Use below global temp table to query the data as you may
select * from ##t

-- **WARNING** Don't forget to drop the global temp table ##t.
--drop table ##t
drop table #d 

Desarrollado y probado en la versión de SQL Server - Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (Build 17134 :)

Puede ajustar el esquema para la versión de su servidor SQL que está utilizando (si es necesario).

vendettamit
fuente
4

Si conoce los parámetros que se están pasando y si no tiene acceso para hacer sp_configure, edite el procedimiento almacenado con estos parámetros y los mismos se pueden almacenar en una tabla global ##.

lakshmivisalij
fuente
3

Esto se puede hacer en SQL Server 2014+ siempre que el procedimiento almacenado solo devuelva una tabla. Si alguien encuentra una manera de hacerlo para varias tablas, me encantaría saberlo.

DECLARE @storedProcname NVARCHAR(MAX) = ''
SET @storedProcname = 'myStoredProc'

DECLARE @strSQL AS VARCHAR(MAX) = 'CREATE TABLE myTableName '

SELECT @strSQL = @strSQL+STUFF((
SELECT ',' +name+' ' + system_type_name 
FROM sys.dm_exec_describe_first_result_set_for_object (OBJECT_ID(@storedProcname),0)
FOR XML PATH('')
),1,1,'(') + ')'

EXEC (@strSQL)

INSERT INTO myTableName

EXEC ('myStoredProc @param1=1, @param2=2')

SELECT * FROM myTableName

DROP TABLE myTableName

Esto extrae la definición de la tabla devuelta de las tablas del sistema, y ​​la usa para construir la tabla temporal por usted. Luego puede rellenarlo desde el procedimiento almacenado como se indicó anteriormente.

También hay variantes de esto que también funcionan con Dynamic SQL.

Matthew Baker
fuente
2

Unos años tarde a la pregunta, pero necesitaba algo como esto para generar un código rápido y sucio. Creo que, como otros han dicho, es más fácil definir la tabla temporal por adelantado, pero este método debería funcionar para consultas de procedimientos almacenados simples o declaraciones SQL.

Esto será un poco complicado, pero toma prestado de los contribuyentes aquí, así como la solución de Paul White de DBA Stack Exchange Obtener tipos de columnas de resultados de procedimientos almacenados . Nuevamente, reiterar este enfoque y ejemplo no está diseñado para procesos en un entorno multiusuario. En este caso, la definición de la tabla se establece por un corto tiempo en una tabla temporal global para referencia por un proceso de plantilla de generación de código.

No he probado completamente esto, por lo que puede haber advertencias, por lo que es posible que desee ir al enlace de MSDN en la respuesta de Paul White. Esto se aplica a SQL 2012 y superior.

Primero use el procedimiento almacenado sp_describe_first_result_set que se parece a la descripción de Oracle.

Esto evaluará la primera fila del primer conjunto de resultados, por lo que si su procedimiento almacenado o declaración devuelve múltiples consultas, solo describirá el primer resultado.

Creé un proceso almacenado para desglosar las tareas que devuelve un solo campo para seleccionar para crear la definición de la tabla temporal.

CREATE OR ALTER PROCEDURE [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet]
(
     @sql NVARCHAR(4000)
    ,@table_name VARCHAR(100)
    ,@TableDefinition NVARCHAR(MAX) OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @TempTableDefinition NVARCHAR(MAX)
    DECLARE @NewLine NVARCHAR(4) = CHAR(13)+CHAR(10)

    DECLARE @ResultDefinition TABLE (  --The View Definition per MSDN
      is_hidden         bit NOT NULL
    , column_ordinal    int NOT NULL
    , [name]            sysname NULL
    , is_nullable       bit NOT NULL
    , system_type_id    int NOT NULL
    , system_type_name  nvarchar(256) NULL
    , max_length        smallint NOT NULL
    , [precision]       tinyint NOT NULL
    , scale             tinyint NOT NULL
    , collation_name    sysname NULL    
    , user_type_id      int NULL
    , user_type_database    sysname NULL    
    , user_type_schema  sysname NULL
    , user_type_name    sysname NULL    
    , assembly_qualified_type_name      nvarchar(4000)  
    , xml_collection_id         int NULL
    , xml_collection_database   sysname NULL    
    , xml_collection_schema     sysname NULL    
    , xml_collection_name       sysname NULL
    , is_xml_document           bit NOT NULL            
    , is_case_sensitive         bit NOT NULL            
    , is_fixed_length_clr_type  bit NOT NULL    
    , source_server             sysname NULL            
    , source_database           sysname NULL
    , source_schema             sysname NULL
    , source_table              sysname NULL
    , source_column             sysname NULL
    , is_identity_column        bit NULL
    , is_part_of_unique_key     bit NULL
    , is_updateable             bit NULL
    , is_computed_column        bit NULL
    , is_sparse_column_set      bit NULL
    , ordinal_in_order_by_list  smallint NULL   
    , order_by_is_descending    smallint NULL   
    , order_by_list_length      smallint NULL
    , tds_type_id               int NOT NULL
    , tds_length                int NOT NULL
    , tds_collation_id          int NULL
    , tds_collation_sort_id     tinyint NULL
    )

    --Insert the description into table variable    
    INSERT @ResultDefinition
    EXEC sp_describe_first_result_set @sql

    --Now Build the string to create the table via union select statement
    ;WITH STMT AS (
        SELECT N'CREATE TABLE ' + @table_name + N' (' AS TextVal
        UNION ALL

        SELECT 
         CONCAT(
                CASE column_ordinal
                    WHEN 1 THEN '     ' ELSE '   , ' END  --Determines if comma should precede
                , QUOTENAME([name]) , '   ', system_type_name  -- Column Name and SQL TYPE
                ,CASE is_nullable 
                    WHEN 0 THEN '   NOT NULL' ELSE '   NULL' END --NULLABLE CONSTRAINT          
               ) AS TextVal
        FROM @ResultDefinition WHERE is_hidden = 0  -- May not be needed
        UNION ALL

        SELECT N');' + @NewLine
    ) 

    --Now Combine the rows to a single String
    SELECT @TempTableDefinition = COALESCE (@TempTableDefinition + @NewLine + TextVal, TextVal) FROM STMT

    SELECT @TableDefinition = @TempTableDefinition
END

El enigma es que necesita usar una tabla global, pero debe hacerla lo suficientemente única para que pueda soltarla y crearla con frecuencia sin preocuparse por una colisión.
En el ejemplo, utilicé un Guid (FE264BF5_9C32_438F_8462_8A5DC8DEE49E) para la variable global que reemplaza los guiones con guión bajo

DECLARE @sql NVARCHAR(4000) = N'SELECT @@SERVERNAME as ServerName, GETDATE() AS Today;'
DECLARE @GlobalTempTable VARCHAR(100) = N'##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable'

--@sql can be a stored procedure name like dbo.foo without parameters

DECLARE @TableDef NVARCHAR(MAX)

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

EXEC [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet] 
    @sql, @GlobalTempTable, @TableDef OUTPUT

--Creates the global table ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
EXEC sp_executesql @TableDef 

--Now Call the stored procedure, SQL Statement with Params etc.
INSERT ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
    EXEC sp_executesql @sql 

--Select the results into your undefined Temp Table from the Global Table
SELECT * 
INTO #MyTempTable
FROM ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

SELECT * FROM #MyTempTable

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

Nuevamente, solo lo probé con consultas simples de procedimientos almacenados y consultas simples, por lo que su kilometraje puede variar. Espero que esto ayude a alguien.

Charles Byrne
fuente
1

Bueno, debe crear una tabla temporal, pero no tiene que tener el esquema correcto ... He creado un procedimiento almacenado que modifica una tabla temporal existente para que tenga las columnas necesarias con los datos correctos. tipo y orden (descartar todas las columnas existentes, agregar nuevas columnas):

GO
create procedure #TempTableForSP(@tableId int, @procedureId int)  
as   
begin  
    declare @tableName varchar(max) =  (select name  
                                        from tempdb.sys.tables 
                                        where object_id = @tableId
                                        );    
    declare @tsql nvarchar(max);    
    declare @tempId nvarchar(max) = newid();      
    set @tsql = '    
    declare @drop nvarchar(max) = (select  ''alter table tempdb.dbo.' + @tableName 
            +  ' drop column ''  + quotename(c.name) + '';''+ char(10)  
                                   from tempdb.sys.columns c   
                                   where c.object_id =  ' + 
                                         cast(@tableId as varchar(max)) + '  
                                   for xml path('''')  
                                  )    
    alter table tempdb.dbo.' + @tableName + ' add ' + QUOTENAME(@tempId) + ' int;
    exec sp_executeSQL @drop;    
    declare @add nvarchar(max) = (    
                                select ''alter table ' + @tableName 
                                      + ' add '' + name 
                                      + '' '' + system_type_name 
                           + case when d.is_nullable=1 then '' null '' else '''' end 
                                      + char(10)   
                              from sys.dm_exec_describe_first_result_set_for_object(' 
                               + cast(@procedureId as varchar(max)) + ', 0) d  
                                order by column_ordinal  
                                for xml path(''''))    

    execute sp_executeSQL  @add;    
    alter table '  + @tableName + ' drop column ' + quotename(@tempId) + '  ';      
    execute sp_executeSQL @tsql;  
end         
GO

create table #exampleTable (pk int);

declare @tableId int = object_Id('tempdb..#exampleTable')
declare @procedureId int = object_id('examplestoredProcedure')

exec #TempTableForSP @tableId, @procedureId;

insert into #exampleTable
exec examplestoredProcedure

Tenga en cuenta que esto no funcionará si sys.dm_exec_describe_first_result_set_for_object no puede determinar los resultados del procedimiento almacenado (por ejemplo, si usa una tabla temporal).

jmoreno
fuente
0

Si deja que el SQL dinámico cree una tabla temporal, esta tabla es propiedad de la conexión SQL dinámica, a diferencia de la conexión desde la que se llama a su procedimiento almacenado.

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;

ingrese la descripción de la imagen aquí

DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

SELECT * FROM #Pivoted;

Mensaje 208, Nivel 16, Estado 0 Nombre de objeto inválido '#Pivoted'. Esto se debe a que #Pivoted es propiedad de la conexión Dynamic SQL. Entonces la última instrucción

SELECT * FROM #Pivoted

falla

Una forma de no enfrentar este problema es asegurarse de que todas las referencias a #Pivoted se hagan desde el interior de la consulta dinámica:

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;


DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
SELECT * FROM #Pivoted;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

ingrese la descripción de la imagen aquí

Ludovic Aubert
fuente
-5

Yo haría lo siguiente

  1. Cree (convierta SP a) un UDF (valor de tabla UDF).

  2. select * into #tmpBusLine from dbo.UDF_getBusinessLineHistory '16 Mar 2009'

Hlin
fuente
2
Puede haber algunos obstáculos para dar el primer paso. Por ejemplo, si el SP original usa tablas temporales. Las UDF no pueden usar tablas temporales.
2014