Inserir resultados de um procedimento armazenado em uma tabela temporária

1579

Como eu faço um SELECT * INTO [temp table] FROM [stored procedure]? Não FROM [Table]e sem definir [temp table]?

Selecttodos os dados do BusinessLinepara dentro tmpBusLinefuncionam bem.

select *
into tmpBusLine
from BusinessLine

Estou tentando o mesmo, mas usando um stored procedureque retorna dados, não é exatamente o mesmo.

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

Mensagem de saída:

Msg 156, Nível 15, Estado 1, Linha 2 Sintaxe incorreta perto da palavra-chave 'exec'.

Li vários exemplos de criação de uma tabela temporária com a mesma estrutura que o procedimento armazenado de saída, que funciona bem, mas seria bom não fornecer nenhuma coluna.

Ferdeen
fonte
22
Com SELECT * INTO [NOME DA TABELA], você conhece as colunas, pois elas são copiadas da tabela original. É exatamente isso que eu quero se fizesse a mesma coisa contra um procedimento armazenado.
28709 Ferdeen
7
Só quero salientar que "selecionar * em tmpBusLine" cria uma tabela permanente. Você provavelmente deseja "selecionar * em #tmpBusLine". Tenho certeza de que o cartaz original já descobriu isso, mas pode ajudar os outros que acham este post, pois é o primeiro resultado atualmente para a pesquisa "selecione na tabela temp"
ktam33
2
Não sei se isso foi resolvido ou não, mas a razão pela qual você recebe o erro é por causa da palavra-chave from.
Wes Palmer
9
A Microsoft precisa adicionar SELECT * INTO FROM EXEC! Por favor!
Kjmerf # 25/18

Respostas:

704

Você pode usar o OPENROWSET para isso. Dar uma olhada. Também incluí o código sp_configure para ativar as consultas distribuídas ad hoc, caso ainda não esteja ativado.

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
fonte
28
Este é o caminho certo para fazê-lo. OPENROWSET é praticamente a única maneira de tratar os resultados de um procedimento armazenado como uma expressão de tabela.
23430 Rob Farley
37
Isso parece um pouco complicado apenas para inserir em uma tabela. Muita configuração para fazer. Além disso, quando tentei, obtive "Msg 7357, Nível 16, Estado 2, Linha 1 Não é possível processar o objeto" EXEC GetPartyAnalysisData 146 ". O provedor OLE DB" SQLNCLI "para servidor vinculado" (nulo) "indica que o objeto tem nenhuma coluna ou o usuário atual não tem permissões nesse objeto ". Então, você precisa configurar um servidor ligado ...
Ferdeen
10
Você não precisa de um servidor vinculado, mas precisa acertar a cadeia de conexão ... e também especificar o caminho completo para o procedimento armazenado, incluindo o nome do banco de dados e o proprietário do sp.
MartW
18
eeeeew! uma referência para o mesmo servidor? desagradável. definitivamente mais de um hack que ter de criar manualmente a tabela temporária
Tim Abell
23
Concordo que este é um hack e provavelmente deve ser evitado, a menos que suas costas estejam contra a parede. Mudar sp para uma função é provavelmente um ângulo melhor a ser adotado. NA MINHA HUMILDE OPINIÃO.
Greg
624

Se você quiser fazer isso sem declarar primeiro a tabela temporária, tente criar uma função definida pelo usuário em vez de um procedimento armazenado e faça com que essa função definida pelo usuário retorne uma tabela. Como alternativa, se você deseja usar o procedimento armazenado, tente algo como isto:

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'
Gavin
fonte
171
Eu acho que o objetivo era gerar o esquema sem ter que declarar explicitamente.
26712 Craig
5
Gostaria de saber qual a diferença entre essa e a solução do @Aaron Alton acima. Este parece muito mais simples, mas não tenho certeza sobre outras implicações.
funkymushroom
11
Isso funcionará, mas se você adicionar colunas adicionais ao procedimento armazenado SpGetRecords, isso explodirá.
Brady Holt
15
Você recebe apenas um INSERT INTO EXEC por pilha de chamadas. O SpGetRecords e qualquer outro processo chamado não podem usar essa estratégia em seu próprio código. Isso pode surpreender os mantenedores do SpGetRecords.
Matt Stephenson
33
Isso não responde à pergunta e não vejo por que ela é tão votada? O OP declarou explicitamente "sem definir [tabela temporária]" e sua primeira linha possui uma instrução criar tabela temporária.
NickG
296

No SQL Server 2005, você pode usar INSERT INTO ... EXECpara inserir o resultado de um procedimento armazenado em uma tabela. Na documentação do MSDNINSERT (para o SQL Server 2000, de fato):

--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales
Matt Hamilton
fonte
122
Isso requer que o author_sales seja definido com antecedência. Eu estou tentando evitar isso. Obrigado.
28409 Ferdeen
5
Foi bem o que pensei. Tão útil Inserir nas tabelas tmp rapidamente, mas não tão útil se você precisar conhecer a estrutura do conjunto de dados retornada de um processo armazenado. Obrigado pela ajuda.
28409 Ferdeen
3
Há um bom artigo aqui msdn.microsoft.com/en-us/library/aa175921.aspx
rico Andrews
4
Para usar o mesmo esquema, você pode fazer uma cópia da seguinte maneira: selecione top 0 * em tempTable a partir de realTable ( stackoverflow.com/a/9206463/73794 )
Even Mien
@EvenMien Fiquei momentaneamente empolgado quando vi seu comentário ... mas, infelizmente, isso só funciona se os resultados do seu processo realmente refletem uma tabela real :(
BVernon
193

Esta é uma resposta para uma versão ligeiramente modificada da sua pergunta. Se você pode abandonar o uso de um procedimento armazenado para uma função definida pelo usuário, pode usar uma função definida pelo usuário embutida com valor de tabela. Este é essencialmente um procedimento armazenado (aceita parâmetros) que retorna uma tabela como um conjunto de resultados; e, portanto, colocará bem com uma declaração INTO.

Aqui está um bom artigo rápido sobre ele e outras funções definidas pelo usuário. Se você ainda precisa de um procedimento armazenado, é possível agrupar a função definida pelo usuário em linha com valor de tabela com um procedimento armazenado. O procedimento armazenado apenas passa parâmetros quando chama select * da função definida pelo usuário com valor de tabela embutido.

Por exemplo, você teria uma função definida pelo usuário em linha com valor de tabela para obter uma lista de clientes para uma região específica:

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

Em seguida, você pode chamar esta função para obter os resultados como:

SELECT * FROM CustomersbyRegion(1)

Ou para fazer um SELECT INTO:

SELECT * INTO CustList FROM CustomersbyRegion(1)

Se você ainda precisar de um procedimento armazenado, envolva a função da seguinte maneira:

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

Eu acho que este é o método mais 'hack-less' para obter os resultados desejados. Ele usa os recursos existentes, pois eles foram projetados para serem usados ​​sem complicações adicionais. Aninhando a função definida pelo usuário em linha com valor de tabela no procedimento armazenado, você tem acesso à funcionalidade de duas maneiras. Mais! Você tem apenas um ponto de manutenção para o código SQL real.

O uso do OPENROWSET foi sugerido, mas não é para isso que a função OPENROWSET se destina a ser usada (nos Manuais Online):

Inclui todas as informações de conexão necessárias para acessar dados remotos de uma fonte de dados OLE DB. Esse método é uma alternativa ao acesso a tabelas em um servidor vinculado e é um método ad hoc único de conexão e acesso a dados remotos usando o OLE DB. Para referências mais frequentes a fontes de dados OLE DB, use servidores vinculados.

O uso do OPENROWSET fará o trabalho, mas haverá uma sobrecarga adicional para abrir conexões locais e organizar dados. Também pode não ser uma opção em todos os casos, pois requer uma permissão de consulta ad hoc, que representa um risco à segurança e, portanto, pode não ser desejada. Além disso, a abordagem OPENROWSET impedirá o uso de procedimentos armazenados retornando mais de um conjunto de resultados. O agrupamento de várias funções embutidas definidas pelo usuário com valor de tabela em um único procedimento armazenado pode conseguir isso.

Christian Loris
fonte
4
+1 Uma função com valor de tabela é uma solução apropriada. Devemos observar as pequenas desvantagens: a função com valor de tabela é um objeto extra de banco de dados e pode ser necessário conceder privilégios a ele.
Spencer7593
2
Adoro a solução. Um pequeno obstáculo que acertei é que minha tabela não pode ter ordem em que lugar ela poderia estar no procedimento armazenado. Owh, bem, eu vou resolver isso #
4111 mrwaim
5
Mais um obstáculo - "Não é possível acessar tabelas temporárias dentro de uma função"
mrwaim
7
A pergunta original é como criamos uma tabela temporária com os resultados do sp. Este é um padrão bom, mas o endereço doesnt esta pergunta
greg
16
greg, a primeira linha da minha resposta declara "Esta é uma resposta para uma versão ligeiramente modificada da sua pergunta." O seu comentário é redundante.
Christian Loris
131
EXEC sp_serveroption 'YOURSERVERNAME', 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpTable
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.sproc 1')
Quassnoi
fonte
2
Obter um "Msg 208, nível 16, estado 1, linha 1 Nome de objecto inválido 'tmpBusLine' (provavelmente como não é definir na frente).
Ferdeen
1
@Ferds: desculpe, não entendi sua solicitação primeiro. Atualizado com outra solução.
Quassnoi 04/08/09
26
Ótima solução. Uma ressalva, você precisará habilitar 'DATA ACCESS' no seu servidor: EXEC sp_serveroption 'TheServerName', 'DATA ACCESS', TRUE
jcollum
8
Você também precisará permitir acesso remoto ao servidor. Isso terá ramificações de segurança.
BraveNewMath
7
Isso não funcionará se o procedimento armazenado de destino usar tabelas temporárias
Sal
125

Solução mais fácil:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

Se você não conhece o esquema, pode fazer o seguinte. Observe que existem riscos graves à segurança nesse método.

SELECT * 
INTO #temp
FROM OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [db].[schema].[sproc]')
Tigerjz32
fonte
se eu não conhecer a coluna do conjunto de resultados retornado, então ??? Quero dizer coluna pode variar. Então, como inserir o resultado na tabela temporária ???
SHEKHAR SHETE
Você pode usar o OPENQUERY, mas não é recomendado, pois vem com falhas de segurança.
precisa saber é o seguinte
1
"se eu não conhecer a coluna do conjunto de resultados retornado", então você não poderá usá-lo em sua lógica. Como você usará os dados se não sabe o que é?
Adriaan Davel
@AdriaanDavel Concordo com você que você deve sempre conhecer seus dados (melhor prática), no entanto, o que ele pode estar dizendo é que há momentos em que o sproc retorna colunas dinâmicas e você nem sempre sabe como será o esquema. Nesse caso, você pode usar o OPENROWSET para inserir e criar uma tabela rapidamente. No entanto, existem riscos de segurança óbvias em fazer isso ...
Tigerjz32
1
@ urettin Às vezes você não sabe o que o procedimento armazenado retornará. O que acontece nesse caso? Como você pode criar uma tabela temporária (quando não sabe o que o procedimento armazenado retornará) e inserir nela a partir de um procedimento armazenado?
Tigerjz32
106

Quando o procedimento armazenado retorna muitas colunas e você não deseja "criar" manualmente uma tabela temporária para conter o resultado, descobri que a maneira mais fácil é entrar no procedimento armazenado e adicionar uma cláusula "into" no diretório última instrução de seleção e adicione 1 = 0 à cláusula where.

Execute o procedimento armazenado uma vez e volte e remova o código SQL que você acabou de adicionar. Agora, você terá uma tabela vazia correspondente ao resultado do procedimento armazenado. Você pode "script table as create" para uma tabela temporária ou simplesmente inserir diretamente nessa tabela.

dotjoe
fonte
9
+1, excelente sugestão. Você pode até adicionar uma variável opcional rápida ao sproc chamada @TableCreate ou algo semelhante que, quando não for nulo, execute as etapas acima. Não requer a troca do sproc depois que ele estiver configurado.
22630 Ian Roke
1
@dotjoe Você cria SELECT INTOuma tabela temporária e uma tabela de script criada a partir da tabela temporária? As tabelas temporárias aparecem, tempdbmas não consigo clicar com o botão direito e criar um script. Qualquer ajuda é apreciada.
DotnetDude
2
@DotNetDude, você pode select ... into new_tablecriar implicitamente uma tabela real.
dotjoe
Em seguida, pegue a definição aproximada da coluna do esquema da tabela vazia; substitua '...' no final por 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
TABLE_NAME
Essa é a melhor solução!
precisa saber é o seguinte
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
fonte
3
não aborda a questão original do OP, fazendo a inserção sem definir a tabela temporária primeiro.
21417
48

Seu procedimento armazenado apenas recupera os dados ou também os modifica? Se for usado apenas para recuperar, você poderá converter o procedimento armazenado em uma função e usar as Common Table Expressions (CTEs) sem precisar declá-lo, da seguinte maneira:

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

No entanto, o que precisar ser recuperado do CTE deve ser usado em apenas uma instrução. Você não pode fazer um with temp as ...e tentar usá-lo após algumas linhas do SQL. Você pode ter vários CTEs em uma instrução para consultas mais complexas.

Por exemplo,

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)
Usuário SO
fonte
1
Estas não são tabelas temporárias, são CTEs. technet.microsoft.com/en-us/library/…
yucer
5
Graças @yucer ... Eu acredito que eu não sabia que eles eram chamados de CTEs volta então :)
SO Usuário
48

Se a tabela de resultados do seu processo armazenado for muito complicada para digitar manualmente a instrução "create table" e você não puder usar OPENQUERY OU OPENROWSET, poderá usar sp_help para gerar a lista de colunas e tipos de dados para você. Depois de ter a lista de colunas, é apenas uma questão de formatá-la para atender às suas necessidades.

Etapa 1: adicione "na #temp" à consulta de saída (por exemplo, "selecione [...] na #temp de [...]").

A maneira mais fácil é editar a consulta de saída no proc diretamente. se você não conseguir alterar o processo armazenado, poderá copiar o conteúdo para uma nova janela de consulta e modificar a consulta lá.

Etapa 2: execute sp_help na tabela temporária. (por exemplo, "exec tempdb..sp_help #temp")

Após criar a tabela temporária, execute sp_help na tabela temporária para obter uma lista das colunas e tipos de dados, incluindo o tamanho dos campos varchar.

Etapa 3: Copie as colunas e os tipos de dados em uma instrução create table

Eu tenho uma planilha do Excel que eu uso para formatar a saída de sp_help em uma instrução "criar tabela". Você não precisa de nada tão sofisticado, basta copiar e colar no seu editor SQL. Use os nomes, tamanhos e tipos de coluna para construir uma instrução "Criar tabela nº x [...]" ou "declarar @x tabela [...]" que você pode usar para INSERIR os resultados do procedimento armazenado.

Etapa 4: Inserir na tabela recém-criada

Agora você terá uma consulta semelhante às outras soluções descritas neste segmento.

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

INSERT INTO @t 
Exec spMyProc 

Essa técnica também pode ser usada para converter uma tabela temporária ( #temp) em uma variável de tabela ( @temp). Embora isso possa ser mais etapas do que apenas escrever a create tableinstrução, evita erros manuais, como erros de digitação e incompatibilidade de tipos de dados em grandes processos. Depurar um erro de digitação pode levar mais tempo do que escrever a consulta em primeiro lugar.

FistOfFury
fonte
37

Se o OPENROWSET estiver causando problemas, existe outra maneira a partir de 2012; use sys.dm_exec_describe_first_result_set_for_object, conforme mencionado aqui: Recuperar nomes de colunas e tipos de um procedimento armazenado?

Primeiro, crie este procedimento armazenado para gerar o SQL para a tabela temporária:

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 o procedimento, chame-o da seguinte maneira:

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

Observe que estou usando uma tabela temporária global. Isso ocorre porque o uso do EXEC para executar o SQL dinâmico cria sua própria sessão; portanto, uma tabela temporária comum estaria fora do escopo de qualquer código subsequente. Se uma tabela temporária global for um problema, você poderá usar uma tabela temporária comum, mas qualquer SQL subsequente precisará ser dinâmico, ou seja, também executado pela instrução EXEC.

StuartQ
fonte
4
Você esqueceu de criar a tabela @SQL.
Trisped
32

Quassnoi me levou a maior parte do caminho até lá, mas faltava uma coisa:

**** Eu precisava usar parâmetros no procedimento armazenado. ****

E o OPENQUERY não permite que isso ocorra:

Então, eu encontrei uma maneira de trabalhar com o sistema e também não tenho que tornar a definição da tabela tão rígida e redefini-la em outro procedimento armazenado (e, claro, arriscar-se).

Sim, você pode criar dinamicamente a definição de tabela retornada do procedimento armazenado usando a instrução OPENQUERY com falsos variáveis ​​(desde que NO NO RESULT SET retorne o mesmo número de campos e na mesma posição que um conjunto de dados com bons dados).

Depois que a tabela é criada, você pode usar o procedimento armazenado exec na tabela temporária o dia inteiro.


E para observar (como indicado acima), você deve ativar o acesso a dados,

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

Obrigado pelas informações que foram fornecidas originalmente ... Sim, finalmente, não preciso criar todas essas definições de tabela (estritas) falsas ao usar dados de outro procedimento armazenado ou banco de dados, e sim, você também pode usar parâmetros.

Tags de referência de pesquisa:

  • Procedimento armazenado do SQL 2005 na tabela temporária

  • openquery com procedimento armazenado e variáveis ​​2005

  • openquery com variáveis

  • executar procedimento armazenado na tabela temporária

Atualização: isso não funcionará com tabelas temporárias, então tive que recorrer à criação manual da tabela temporária.

Aviso chatice : isso não funcionará com tabelas temporárias , http://www.sommarskog.se/share_data.html#OPENQUERY

Referência: O próximo passo é definir LOCALSERVER. Pode parecer uma palavra-chave no exemplo, mas na verdade é apenas um nome. É assim que se faz:

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

Para criar um servidor vinculado, você deve ter a permissão ALTER ANY SERVER ou ser membro de qualquer uma das funções fixas do servidor sysadmin ou setupadmin.

OPENQUERY abre uma nova conexão com o SQL Server. Isso tem algumas implicações:

O procedimento que você chama com OPENQUERY não pode consultar tabelas temporárias criadas na conexão atual.

A nova conexão possui seu próprio banco de dados padrão (definido com sp_addlinkedserver, o padrão é mestre), portanto, toda especificação de objeto deve incluir um nome de banco de dados.

Se você tiver uma transação aberta e estiver retendo bloqueios ao ligar para OPENQUERY, o procedimento chamado não poderá acessar o que você bloqueou. Ou seja, se você não for cuidadoso, irá se bloquear.

A conexão não é de graça, portanto há uma penalidade de desempenho.

Doug Lubey da Louisiana
fonte
1
Se você não souber o nome do servidor, use SELECT @@SERVERNAME. Você também pode usarEXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
Contango
24

Se você tiver a sorte de ter o SQL 2012 ou superior, poderá usar dm_exec_describe_first_result_set_for_object

Acabei de editar o sql fornecido pelo gotqn. Obrigado gotqn.

Isso cria uma tabela temporária global com o mesmo nome que o nome do procedimento. A tabela temporária pode ser usada posteriormente, conforme necessário. Só não se esqueça de largar antes de executar novamente.

    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
fonte
1
Excelente! Apenas uma observação: use em sys.all_objectsvez de sys.proceduresse você quiser fazer isso para procedimentos armazenados internos.
Gert Arnold
2
Isso também falhará se o SP usar tabelas temporárias dentro dele. (mas o seu muito útil para ter isso como uma proc em seu arsenal)
Trubs
23

Este procedimento armazenado faz o trabalho:

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

É um pequeno retrabalho disso: insira os resultados do procedimento armazenado na tabela para que realmente funcione.

Se você deseja que ele funcione com uma tabela temporária, precisará usar uma ##GLOBALtabela e soltá-la posteriormente.

satnhak
fonte
17

Para inserir o primeiro conjunto de registros de um procedimento armazenado em uma tabela temporária, você precisa saber o seguinte:

  1. somente o primeiro conjunto de linhas do procedimento armazenado pode ser inserido em uma tabela temporária
  2. o procedimento armazenado não deve executar a instrução T-SQL dinâmica ( sp_executesql)
  3. você precisa definir a estrutura da tabela temporária primeiro

O exemplo acima pode parecer uma limitação, mas IMHO faz perfeitamente sentido - se você estiver usando, sp_executesqlpoderá retornar duas colunas e uma vez dez e, se tiver vários conjuntos de resultados, não poderá inseri-los em várias tabelas também - poderá inserir o máximo em duas tabelas em uma instrução T-SQL (usando a OUTPUTcláusula e sem gatilhos).

Portanto, a questão é principalmente como definir a estrutura da tabela temporária antes de executar a EXEC ... INTO ...instrução.

O primeiro funciona com OBJECT_IDenquanto o segundo e o terceiro também funcionam com consultas ad-hoc. Prefiro usar o DMV em vez do sp, pois você pode usar CROSS APPLYe criar as definições da tabela temporária para vários procedimentos ao mesmo tempo.

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;

Além disso, preste atenção ao system_type_namecampo, pois pode ser muito útil. Ele armazena a definição completa da coluna. Por exemplo:

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

e você pode usá-lo diretamente na maioria dos casos para criar a definição da tabela.

Portanto, acho que na maioria dos casos (se o procedimento armazenado corresponder a determinados critérios), você poderá criar facilmente instruções dinâmicas para resolver esses problemas (criar a tabela temporária, inserir o resultado do procedimento armazenado, fazer o que for necessário com os dados) .


Observe que os objetos acima falham ao definir os dados do primeiro conjunto de resultados em alguns casos, como quando instruções T-SQL dinâmicas são executadas ou tabelas temporárias são usadas no procedimento armazenado.

gotqn
fonte
observação prática sobre limitações: se você precisar inserir a saída de alguns sp (vamos chamá-lo de SP_LEVEL_0) na tabela temporária criada dinamicamente usando a abordagem acima em outro sp (vamos chamá-lo de SP_LEVEL_1), não será possível fazer o mesmo truque para a saída deste SP_LEVEL_1 para alguma outra tabela temporária em SP_LEVEL_2
nahab 22/09/16
17
  1. Estou criando uma tabela com o seguinte esquema e dados.
  2. Crie um procedimento armazenado.
  3. Agora eu sei qual é o resultado do meu procedimento, então estou executando a seguinte 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] Ativado

    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
fonte
15

Se a consulta não contiver parâmetro, use OpenQueryelse use OpenRowset.

O básico seria criar o esquema conforme o procedimento armazenado e inserir nessa tabela. por exemplo:

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 problema
fonte
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

Eu espero que isso ajude. Qualifique conforme apropriado.

SoftwareARM
fonte
11

Encontrei Passing Arrays / DataTables em Stored Procedures, o que pode lhe dar uma outra idéia de como você pode resolver seu problema.

O link sugere usar um parâmetro do tipo Imagem para passar para o procedimento armazenado. Em seguida, no procedimento armazenado, a imagem é transformada em uma variável de tabela que contém os dados originais.

Talvez exista uma maneira de isso ser usado com uma tabela temporária.

kevchadders
fonte
4
Isso não é mais necessário nas versões Sql2008 e posterior com a introdução dos Parâmetros de valor de tabela . Agora você pode passar diretamente um objeto .net conjunto de dados ou tabela de dados para um procedimento armazenado sql com ter que fazer a conversão para byte como mencionado no link acima
EndlessSpace
10

Eu encontrei o mesmo problema e aqui está o que fiz por sugestão da Paul . A principal parte é aqui é usar NEWID()para evitar que vários usuários executem os procedimentos / scripts de armazenamento ao mesmo tempo, a dor da tabela temporária 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
fonte
9

Outro método é criar um tipo e usar PIPELINED para retornar seu objeto. Isso é limitado a conhecer as colunas no entanto. Mas tem a vantagem de poder fazer:

SELECT * 
FROM TABLE(CAST(f$my_functions('8028767') AS my_tab_type))
Pierre
fonte
O que é isso? Parece não ter nada a ver com o SQL Server que trata esta questão
Martin Smith
8

É um processo simples de duas etapas: - criar uma tabela temporária - inserir na tabela temporária.

Código para executar o mesmo:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;
S Krishna
fonte
Votado; muito semelhante às respostas existentes.
iokevins
6

Depois de pesquisar, encontrei uma maneira de criar uma tabela temporária dinamicamente para qualquer procedimento armazenado sem usar OPENROWSETou OPENQUERYusar um esquema genérico da definição de resultado do Procedimento Armazenado, especialmente quando você não é administrador de banco de dados.

O servidor sql tem um processo de buit-in sp_describe_first_result_setque pode fornecer um esquema de qualquer conjunto de resultados de procedimentos. Criei uma tabela de esquema a partir dos resultados deste procedimento e defina manualmente todo o campo como 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 

Desenvolvido e testado na versão do Sql Server - Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (Build 17134 :)

Você pode ajustar o esquema da versão do servidor SQL que está usando (se necessário).

vendettamit
fonte
4

Se você conhece os parâmetros que estão sendo passados ​​e se não tem acesso para fazer sp_configure, edite o procedimento armazenado com esses parâmetros e o mesmo pode ser armazenado em uma tabela global ##.

lakshmivisalij
fonte
3

Isso pode ser feito no SQL Server 2014 ou superior, desde que o procedimento armazenado retorne apenas uma tabela. Se alguém encontrar uma maneira de fazer isso para várias tabelas, eu adoraria saber sobre isso.

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

Isso puxa a definição da tabela retornada das tabelas do sistema e a usa para criar a tabela temporária para você. Em seguida, você pode preenchê-lo a partir do procedimento armazenado conforme indicado anteriormente.

Existem também variantes disso que também funcionam com o Dynamic SQL.

Matthew Baker
fonte
2

Alguns anos atrasado para a pergunta, mas eu precisava de algo assim para uma geração de código rápida e suja. Acredito que, como outros já declararam, é mais fácil definir a tabela temporária antecipadamente, mas esse método deve funcionar para consultas simples de procedimentos armazenados ou declarações sql.

Isso será um pouco complicado, mas será emprestado dos colaboradores aqui, bem como da solução de Paul White do DBA Stack Exchange Get-type de colunas de resultado do procedimento armazenado . Novamente, para reiterar essa abordagem, o exemplo não foi projetado para processos em um ambiente multiusuário. Nesse caso, a definição da tabela está sendo definida por um curto período em uma tabela temporária global para referência por um processo de modelo de geração de código.

Eu não testei completamente isso, então pode haver advertências, então você pode querer acessar o link do MSDN na resposta de Paul White. Isso se aplica ao SQL 2012 e superior.

Primeiro, use o procedimento armazenado sp_describe_first_result_set, que se assemelha à descrição do Oracle.

Isso avaliará a primeira linha do primeiro conjunto de resultados, portanto, se o procedimento armazenado ou a instrução retornar várias consultas, descreverá apenas o primeiro resultado.

Criei um proc armazenado para dividir as tarefas que retornam um único campo para selecionar e criar a definição da tabela temporária.

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

O enigma é que você precisa usar uma tabela global, mas precisa torná-la única o suficiente para poder abandoná-la e criá-la com frequência, sem se preocupar com uma colisão.
No exemplo, usei um Guid (FE264BF5_9C32_438F_8462_8A5DC8DEE49E) para a variável global, substituindo os hífens por sublinhado

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

Novamente, eu o testei apenas com consultas simples de procedimentos armazenados e consultas simples, para que sua milhagem possa variar. Espero que isso ajude alguém.

Charles Byrne
fonte
1

Bem, você precisa criar uma tabela temporária, mas não precisa ter o esquema correto .... Criei um procedimento armazenado que modifica uma tabela temporária existente para que ela tenha as colunas necessárias com os dados corretos tipo e ordem (descartando todas as colunas existentes, adicionando novas colunas):

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

Observe que isso não funcionará se sys.dm_exec_describe_first_result_set_for_object não puder determinar os resultados do procedimento armazenado (por exemplo, se ele usar uma tabela temporária).

jmoreno
fonte
0

Se você permitir que o SQL dinâmico crie uma tabela temporária, essa tabela será de propriedade da conexão Dynamic SQL, ao contrário da conexão da qual o procedimento armazenado é chamado.

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;

insira a descrição da imagem aqui

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;

Msg 208, Nível 16, Estado 0 Nome de objeto inválido '#Pivoted'. Isso ocorre porque o #Pivoted pertence à conexão Dynamic SQL. Então a última instrução

SELECT * FROM #Pivoted

falha.

Uma maneira de não enfrentar esse problema é garantir que todas as referências a #Pivoted sejam feitas dentro da própria 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);

insira a descrição da imagem aqui

Ludovic Aubert
fonte
-5

Eu faria o seguinte

  1. Crie (converta SP para) um UDF (valor da tabela UDF).

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

Hlin
fonte
2
Pode haver algum obstáculo para dar o seu primeiro passo. Por exemplo, se o SP original usar tabelas temporárias. UDFs não podem usar tabelas temporárias.
yucer