Fügen Sie die Ergebnisse einer gespeicherten Prozedur in eine temporäre Tabelle ein

1579

Wie mache ich eine SELECT * INTO [temp table] FROM [stored procedure]? Nicht FROM [Table]und ohne zu definieren[temp table] ?

SelectAlle Daten von BusinessLinein tmpBusLinefunktionieren einwandfrei.

select *
into tmpBusLine
from BusinessLine

Ich versuche das gleiche, aber die Verwendung eines stored procedure, der Daten zurückgibt, ist nicht ganz dasselbe.

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

Ausgabemeldung:

Meldung 156, Ebene 15, Status 1, Zeile 2 Falsche Syntax in der Nähe des Schlüsselworts 'exec'.

Ich habe mehrere Beispiele für das Erstellen einer temporären Tabelle mit derselben Struktur wie die gespeicherte Ausgabeprozedur gelesen, was gut funktioniert, aber es wäre schön, keine Spalten anzugeben.

Ferdeen
quelle
22
Mit SELECT * INTO [TABLE NAME] kennen Sie die Spalten, da sie aus der Originaltabelle kopiert werden. Dies ist genau das, was ich möchte, wenn ich dasselbe gegen eine gespeicherte Prozedur tun würde.
Ferdeen
7
Ich möchte nur darauf hinweisen, dass "select * into tmpBusLine" eine permanente Tabelle erstellt. Sie möchten wahrscheinlich "select * in #tmpBusLine". Ich bin mir sicher, dass das Originalposter dies bereits herausgefunden hat, aber es könnte anderen helfen, die diesen Beitrag finden, da es derzeit das Top-Ergebnis für die Suche "In temporäre Tabelle auswählen" ist
ktam33
2
Ich weiß nicht, ob dies behoben wurde oder nicht, aber der Grund, warum Sie den Fehler erhalten, ist das Schlüsselwort from.
Wes Palmer
9
Microsoft muss SELECT * INTO FROM EXEC hinzufügen! Bitte!
Kjmerf

Antworten:

704

Sie können hierfür OPENROWSET verwenden . Guck mal. Ich habe auch den Code sp_configure eingefügt, um verteilte Ad-hoc-Abfragen zu aktivieren, falls er noch nicht aktiviert ist.

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
quelle
28
Dies ist der richtige Weg. OPENROWSET ist so ziemlich die einzige Möglichkeit, die Ergebnisse einer gespeicherten Prozedur als Tabellenausdruck zu behandeln.
Rob Farley
37
Dies scheint etwas umständlich zu sein, nur um es in eine Tabelle einzufügen. Viel zu konfigurieren. Auch als ich es versuchte, bekam ich "Msg 7357, Level 16, State 2, Line 1 Kann das Objekt" EXEC GetPartyAnalysisData 146 "nicht verarbeiten. Der OLE DB-Provider" SQLNCLI "für Verbindungsserver" (null) "gibt an, dass eines der Objekte vorhanden ist Keine Spalten oder der aktuelle Benutzer hat keine Berechtigungen für dieses Objekt. " Sie müssen also einen Verbindungsserver einrichten ...
Ferdeen
10
Sie benötigen keinen Verbindungsserver, aber Sie müssen die Verbindungszeichenfolge richtig einstellen ... und außerdem den vollständigen Pfad zur gespeicherten Prozedur angeben, einschließlich des Datenbanknamens und des Besitzers des SP.
MartW
18
eeeeew! ein Verweis auf den gleichen Server? böse. definitiv mehr ein Hack als manuell die temporäre Tabelle erstellen zu müssen
Tim Abell
23
Ich bin damit einverstanden, dass dies ein Hack ist und wahrscheinlich vermieden werden sollte, es sei denn, Ihr Rücken ist an der Wand. Das Ändern des sp in eine Funktion ist wahrscheinlich ein besserer Winkel. MEINER BESCHEIDENEN MEINUNG NACH.
Greg
624

Wenn Sie dies tun möchten, ohne zuerst die temporäre Tabelle zu deklarieren, können Sie versuchen, eine benutzerdefinierte Funktion anstelle einer gespeicherten Prozedur zu erstellen und diese benutzerdefinierte Funktion eine Tabelle zurückgeben zu lassen. Wenn Sie die gespeicherte Prozedur verwenden möchten, versuchen Sie alternativ Folgendes:

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'
Gavin
quelle
171
Ich denke, es ging darum, das Schema zu generieren, ohne es explizit deklarieren zu müssen.
Craig
5
Mich würde interessieren, was der Unterschied zwischen dieser und der obigen Lösung von @Aaron Alton ist. Dieser scheint viel einfacher zu sein, aber ich bin mir nicht sicher, welche anderen Auswirkungen dies hat.
Funkymushroom
11
Dies funktioniert, aber wenn Sie der gespeicherten Prozedur von SpGetRecords jemals zusätzliche Spalten hinzufügen, wird dies in die Luft gesprengt.
Brady Holt
15
Sie erhalten nur ein INSERT INTO EXEC pro Aufrufstapel. SpGetRecords und andere von ihm aufgerufene Prozesse verwenden diese Strategie möglicherweise nicht in ihrem eigenen Code. Dies kann die Betreuer von SpGetRecords überraschen.
Matt Stephenson
33
Dies beantwortet die Frage überhaupt nicht und ich verstehe nicht, warum sie so positiv bewertet ist. Das OP hat explizit "ohne Definition von [temporäre Tabelle]" angegeben, und Ihre allererste Zeile enthält eine Anweisung zum Erstellen einer temporären Tabelle.
NickG
296

In SQL Server 2005 können Sie INSERT INTO ... EXECdas Ergebnis einer gespeicherten Prozedur in eine Tabelle einfügen. Aus der INSERTDokumentation von MSDN (tatsächlich für SQL Server 2000):

--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales
Matt Hamilton
quelle
122
Dies erfordert, dass der autors_sales im Voraus definiert wird. Ich versuche das zu vermeiden. Vielen Dank.
Ferdeen
5
Das habe ich mir gedacht. So nützlich Einfügen in tmp-Tabellen im laufenden Betrieb, aber nicht so nützlich, wenn Sie die von einem gespeicherten Prozess zurückgegebene Datensatzstruktur kennen müssen. Danke für deine Hilfe.
Ferdeen
3
Es gibt einen guten Artikel hier msdn.microsoft.com/en-us/library/aa175921.aspx
Rich Andrews
4
Um dasselbe Schema zu verwenden, können Sie eine Kopie wie folgt erstellen: Wählen Sie top 0 * in tempTable von realTable ( stackoverflow.com/a/9206463/73794 )
Even Mien
@EvenMien Ich war für einen Moment aufgeregt, als ich Ihren Kommentar sah ... aber leider funktioniert das nur, wenn die Ergebnisse Ihres Prozesses tatsächlich eine echte Tabelle
widerspiegeln
193

Dies ist eine Antwort auf eine leicht modifizierte Version Ihrer Frage. Wenn Sie die Verwendung einer gespeicherten Prozedur für eine benutzerdefinierte Funktion aufgeben können, können Sie eine benutzerdefinierte Inline-Tabellenwertfunktion verwenden. Dies ist im Wesentlichen eine gespeicherte Prozedur (nimmt Parameter an), die eine Tabelle als Ergebnismenge zurückgibt. und wird daher gut mit einer INTO-Anweisung platziert.

Hier ist ein guter kurzer Artikel darüber und über andere benutzerdefinierte Funktionen. Wenn Sie immer noch einen Fahrbedarf für eine gespeicherte Prozedur haben, können Sie die benutzerdefinierte Inline-Tabellenwertfunktion mit einer gespeicherten Prozedur umschließen. Die gespeicherte Prozedur übergibt nur Parameter, wenn sie select * aus der benutzerdefinierten Inline-Tabellenwertfunktion aufruft.

So hätten Sie beispielsweise eine benutzerdefinierte Inline-Funktion mit Tabellenwert, um eine Liste der Kunden für eine bestimmte Region abzurufen:

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

Sie können diese Funktion dann aufrufen, um die folgenden Ergebnisse zu erhalten:

SELECT * FROM CustomersbyRegion(1)

Oder um ein SELECT IN zu machen:

SELECT * INTO CustList FROM CustomersbyRegion(1)

Wenn Sie noch eine gespeicherte Prozedur benötigen, wickeln Sie die Funktion als solche ein:

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

Ich denke, dies ist die Methode ohne Hack, um die gewünschten Ergebnisse zu erzielen. Es verwendet die vorhandenen Funktionen so, wie sie ohne zusätzliche Komplikationen verwendet werden sollten. Durch Verschachteln der benutzerdefinierten Inline-Tabellenwertfunktion in der gespeicherten Prozedur haben Sie auf zwei Arten Zugriff auf die Funktionalität. Plus! Sie haben nur einen Wartungspunkt für den eigentlichen SQL-Code.

Die Verwendung von OPENROWSET wurde vorgeschlagen, aber dies ist nicht das Ziel der OPENROWSET-Funktion (From Books Online):

Enthält alle Verbindungsinformationen, die für den Zugriff auf Remote-Daten aus einer OLE DB-Datenquelle erforderlich sind. Diese Methode ist eine Alternative zum Zugriff auf Tabellen auf einem Verbindungsserver und eine einmalige Ad-hoc-Methode zum Verbinden und Zugreifen auf Remote-Daten mithilfe von OLE DB. Verwenden Sie stattdessen Verbindungsserver, um häufigere Verweise auf OLE DB-Datenquellen zu erhalten.

Die Verwendung von OPENROWSET erledigt die Aufgabe, verursacht jedoch zusätzlichen Aufwand für das Öffnen lokaler Verbindungen und das Marshalling von Daten. Dies ist möglicherweise auch nicht in allen Fällen eine Option, da eine Ad-hoc-Abfrageberechtigung erforderlich ist, die ein Sicherheitsrisiko darstellt und daher möglicherweise nicht erwünscht ist. Der OPENROWSET-Ansatz schließt auch die Verwendung gespeicherter Prozeduren aus, die mehr als eine Ergebnismenge zurückgeben. Dies kann erreicht werden, indem mehrere benutzerdefinierte Inline-Tabellenwertfunktionen in eine einzige gespeicherte Prozedur eingeschlossen werden.

Christian Loris
quelle
4
+1 Eine tabellenwertige Funktion ist eine geeignete Lösung. Wir sollten die kleinen Nachteile beachten: Die Funktion mit Tabellenwert ist ein zusätzliches Datenbankobjekt, und es kann erforderlich sein, Berechtigungen dafür zu erteilen.
Spencer7593
2
Ich liebe die Lösung. Ein kleiner Haken, den ich getroffen habe, ist, dass meine Tabelle keine Reihenfolge haben kann, nach der sie in der gespeicherten Prozedur liegen könnte. Na gut, ich werde es
klären
5
Noch ein Haken - "Kann nicht innerhalb einer Funktion auf temporäre Tabellen zugreifen"
mrwaim
7
Die ursprüngliche Frage ist, wie wir eine temporäre Tabelle mit den Ergebnissen der sp erstellen. Dies ist ein gutes Muster, aber tut diese Frage zu beantworten
greg
16
Greg, in der ersten Zeile meiner Antwort steht "Dies ist eine Antwort auf eine leicht modifizierte Version Ihrer Frage." Ihr Kommentar ist überflüssig.
Christian Loris
131
EXEC sp_serveroption 'YOURSERVERNAME', 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpTable
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.sproc 1')
Quassnoi
quelle
2
Holen Sie sich eine "Nachricht 208, Ebene 16, Status 1, Zeile 1 Ungültiger Objektname 'tmpBusLine' (wahrscheinlich, da er nicht im
Voraus
1
@Ferds: Entschuldigung, habe deine Anfrage zuerst nicht verstanden. Mit einer anderen Lösung aktualisiert.
Quassnoi
26
Tolle Lösung. Eine Einschränkung: Sie müssen 'DATA ACCESS' auf Ihrem Server aktivieren: EXEC sp_serveroption 'TheServerName', 'DATA ACCESS', TRUE
jcollum
8
Sie müssen auch den Remotezugriff auf den Server zulassen. Dies hat Auswirkungen auf die Sicherheit.
BraveNewMath
7
Dies wird nicht funktionieren, wenn die gespeicherte Zielprozedur temporäre Tabellen verwendet
Sal
125

Einfachste Lösung:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

Wenn Sie das Schema nicht kennen, können Sie Folgendes tun. Bitte beachten Sie, dass diese Methode schwerwiegende Sicherheitsrisiken birgt.

SELECT * 
INTO #temp
FROM OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [db].[schema].[sproc]')
Tigerjz32
quelle
Wenn ich die Spalte der zurückgegebenen Ergebnismenge nicht kenne, dann ??? Ich meine Spalte kann variieren. Wie füge ich das Ergebnis in die temporäre Tabelle ein?
SHEKHAR SHETE
Sie könnten OPENQUERY verwenden, dies wird jedoch nicht empfohlen, da es Sicherheitslücken aufweist.
Tigerjz32
1
"Wenn ich die Spalte der zurückgegebenen Ergebnismenge nicht kenne, dann" dann können Sie sie nicht in Ihrer Logik verwenden. Wie werden Sie die Daten verwenden, wenn Sie nicht wissen, was es ist?
Adriaan Davel
@AdriaanDavel Ich stimme Ihnen zu, dass Sie Ihre Daten immer kennen sollten (Best Practice). Er könnte jedoch sagen, dass der Sproc manchmal dynamische Spalten zurückgibt und Sie nicht immer wissen, wie das Schema aussehen wird. In diesem Fall können Sie mit OPENROWSET eine Tabelle im laufenden Betrieb einfügen und erstellen. Es gibt jedoch offensichtliche Sicherheitsrisiken dabei ...
Tigerjz32
1
@nurettin Manchmal weiß man nicht, was die gespeicherte Prozedur zurückgeben wird. Was passiert in diesem Fall? Wie können Sie eine temporäre Tabelle erstellen (wenn Sie nicht wissen, was die gespeicherte Prozedur zurückgibt) und von einer gespeicherten Prozedur in diese einfügen?
Tigerjz32
106

Wenn die gespeicherte Prozedur viele Spalten zurückgibt und Sie keine temporäre Tabelle manuell erstellen möchten, um das Ergebnis zu speichern, ist es am einfachsten, in die gespeicherte Prozedur zu gehen und eine "in" -Klausel in die hinzuzufügen letzte select-Anweisung und addiere 1 = 0 zur where-Klausel.

Führen Sie die gespeicherte Prozedur einmal aus und entfernen Sie den soeben hinzugefügten SQL-Code. Jetzt haben Sie eine leere Tabelle, die dem Ergebnis der gespeicherten Prozedur entspricht. Sie können entweder "Skripttabelle als erstellen" für eine temporäre Tabelle erstellen oder einfach direkt in diese Tabelle einfügen.

dotjoe
quelle
9
+1, ausgezeichneter Vorschlag. Sie können dem Sproc sogar eine schnelle optionale Variable mit dem Namen @TableCreate oder eine ähnliche Variable hinzufügen, die die obigen Schritte ausführt, wenn sie nicht null ist. Es ist nicht erforderlich, den Sproc nach dem Einrichten zu wechseln.
Ian Roke
1
@dotjoe Machst du SELECT INTOeine temporäre Tabelle und eine Skripttabelle, wie sie aus der temporären Tabelle erstellt wird? Temp-Tabellen werden in angezeigt, tempdbaber ich kann keinen Rechtsklick ausführen und kein Erstellungsskript erstellen. Jede Hilfe wird geschätzt.
DotnetDude
2
@DotNetDude Sie können select ... into new_tableimplizit eine tatsächliche Tabelle erstellen.
Dotjoe
Nehmen Sie dann eine grobe Spaltendefinition aus dem leeren Tabellenschema. Ersetzen Sie '...' am Ende durch legit TABLE_NAME: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
Dies ist die beste Lösung!
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;
Nitin
quelle
3
geht nicht auf die ursprüngliche OP-Frage ein und führt die Einfügung durch, ohne zuerst die temporäre Tabelle zu definieren.
t.durden
48

Ruft Ihre gespeicherte Prozedur nur die Daten ab oder ändert sie sie auch? Wenn es nur zum Abrufen verwendet wird, können Sie die gespeicherte Prozedur in eine Funktion konvertieren und die Common Table Expressions (CTEs) verwenden, ohne sie wie folgt deklarieren zu müssen:

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

Was jedoch aus dem CTE abgerufen werden muss, sollte nur in einer Anweisung verwendet werden. Sie können nicht tunwith temp as ... und versuchen, es nach ein paar Zeilen SQL zu verwenden. Sie können mehrere CTEs in einer Anweisung für komplexere Abfragen haben.

Zum Beispiel,

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)
SO Benutzer
quelle
1
Dies sind keine temporären Tabellen, sondern CTEs. technet.microsoft.com/en-us/library/…
yucer
5
Danke @yucer ... Ich glaube, ich wusste nicht, dass sie damals CTEs genannt wurden :)
SO User
48

Wenn die Ergebnistabelle Ihres gespeicherten Prozesses zu kompliziert ist, um die Anweisung "create table" manuell einzugeben, und Sie OPENQUERY OR OPENROWSET nicht verwenden können, können Sie mit sp_help die Liste der Spalten und Datentypen für Sie erstellen. Sobald Sie die Liste der Spalten haben, müssen Sie sie nur noch Ihren Anforderungen entsprechend formatieren.

Schritt 1: Fügen Sie der Ausgabeabfrage "in #temp" hinzu (z. B. "Wählen Sie [...] in [temp aus [...]").

Am einfachsten ist es, die Ausgabeabfrage im Prozess direkt zu bearbeiten. Wenn Sie den gespeicherten Prozess nicht ändern können, können Sie den Inhalt in ein neues Abfragefenster kopieren und die Abfrage dort ändern.

Schritt 2: Führen Sie sp_help in der temporären Tabelle aus. (zB "exec tempdb..sp_help #temp")

Führen Sie nach dem Erstellen der temporären Tabelle sp_help für die temporäre Tabelle aus, um eine Liste der Spalten und Datentypen einschließlich der Größe der Varchar-Felder abzurufen.

Schritt 3: Kopieren Sie die Datenspalten und -typen in eine Anweisung zum Erstellen einer Tabelle

Ich habe eine Excel-Tabelle, mit der ich die Ausgabe von sp_help in eine Anweisung "create table" formatiere. Sie brauchen nichts Besonderes, kopieren Sie es einfach und fügen Sie es in Ihren SQL-Editor ein. Verwenden Sie die Spaltennamen, -größen und -typen, um eine Anweisung "Tabelle #x [...] erstellen" oder "Tabelle @x [...] deklarieren" zu erstellen, mit der Sie die Ergebnisse der gespeicherten Prozedur einfügen können.

Schritt 4: In die neu erstellte Tabelle einfügen

Jetzt haben Sie eine Abfrage, die den anderen in diesem Thread beschriebenen Lösungen entspricht.

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

INSERT INTO @t 
Exec spMyProc 

Diese Technik kann auch verwendet werden, um eine temporäre Tabelle ( #temp) in eine Tabellenvariable ( @temp) zu konvertieren . Dies kann mehr als nur das Schreiben der create tableAnweisung selbst sein, verhindert jedoch manuelle Fehler wie Tippfehler und Datentypinkongruenzen in großen Prozessen. Das Debuggen eines Tippfehlers kann länger dauern als das Schreiben der Abfrage.

Faust der Wut
quelle
37

Wenn das OPENROWSET Probleme verursacht, gibt es ab 2012 einen anderen Weg. Verwenden Sie sys.dm_exec_describe_first_result_set_for_object, wie hier erwähnt: Spaltennamen und Typen einer gespeicherten Prozedur abrufen ?

Erstellen Sie zunächst diese gespeicherte Prozedur, um die SQL für die temporäre Tabelle zu generieren:

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 +')'

Um das Verfahren zu verwenden, rufen Sie es folgendermaßen auf:

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

Beachten Sie, dass ich eine globale temporäre Tabelle verwende. Dies liegt daran, dass die Verwendung von EXEC zum Ausführen des dynamischen SQL eine eigene Sitzung erstellt, sodass eine normale temporäre Tabelle für jeden nachfolgenden Code nicht relevant ist. Wenn eine globale temporäre Tabelle ist ein Problem, Sie können eine gewöhnliche temporäre Tabelle verwenden, aber jede nachfolgende SQL müßte dynamisch sein, das heißt, auch durch die EXEC - Anweisung ausgeführt.

StuartQ
quelle
4
Sie haben vergessen, die Tabelle aus zu erstellen @SQL.
Trisped
32

Quassnoi hat mich den größten Teil des Weges dorthin gebracht, aber eines fehlte:

**** Ich musste Parameter in der gespeicherten Prozedur verwenden. ****

Und OPENQUERY lässt dies nicht zu:

Also habe ich einen Weg gefunden, das System zu arbeiten und muss die Tabellendefinition auch nicht so starr machen und sie in einer anderen gespeicherten Prozedur neu definieren (und natürlich die Chance nutzen, dass sie kaputt geht)!

Ja, Sie können die von der gespeicherten Prozedur zurückgegebene Tabellendefinition mithilfe der Anweisung OPENQUERY mit falschen Variablen dynamisch erstellen (solange NO RESULT SET dieselbe Anzahl von Feldern und dieselbe Position wie ein Dataset mit guten Daten zurückgibt).

Sobald die Tabelle erstellt wurde, können Sie die gespeicherte Prozedur exec den ganzen Tag in der temporären Tabelle verwenden.


Und um zu beachten (wie oben angegeben), müssen Sie den Datenzugriff aktivieren,

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

Code:

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

Vielen Dank für die Informationen, die ursprünglich bereitgestellt wurden ... Ja, schließlich muss ich nicht alle diese falschen (strengen) Tabellendefinitionen erstellen, wenn ich Daten aus einer anderen gespeicherten Prozedur oder Datenbank verwende, und ja, Sie können auch Parameter verwenden.

Referenz-Tags suchen:

  • Gespeicherte SQL 2005-Prozedur in temporäre Tabelle

  • openquery mit gespeicherter Prozedur und Variablen 2005

  • openquery mit Variablen

  • Führen Sie die gespeicherte Prozedur in der temporären Tabelle aus

Update: Dies funktioniert nicht mit temporären Tabellen, daher musste ich die temporäre Tabelle manuell erstellen.

Schade : Dies funktioniert nicht mit temporären Tabellen , http://www.sommarskog.se/share_data.html#OPENQUERY

Referenz: Als nächstes definieren Sie LOCALSERVER. Es mag im Beispiel wie ein Schlüsselwort aussehen, aber es ist tatsächlich nur ein Name. Das ist wie man es macht:

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

Um einen Verbindungsserver zu erstellen, müssen Sie über die Berechtigung ALTER ANY SERVER verfügen oder Mitglied einer der festen Serverrollen sysadmin oder setupadmin sein.

OPENQUERY öffnet eine neue Verbindung zu SQL Server. Dies hat einige Auswirkungen:

Die Prozedur, die Sie mit OPENQUERY aufrufen, kann nicht auf temporäre Tabellen verweisen, die in der aktuellen Verbindung erstellt wurden.

Die neue Verbindung verfügt über eine eigene Standarddatenbank (definiert mit sp_addlinkedserver, Standard ist master), daher müssen alle Objektspezifikationen einen Datenbanknamen enthalten.

Wenn Sie eine offene Transaktion haben und beim Aufrufen von OPENQUERY Sperren halten, kann die aufgerufene Prozedur nicht auf das zugreifen, was Sie sperren. Das heißt, wenn Sie nicht aufpassen, werden Sie sich selbst blockieren.

Das Verbinden ist nicht kostenlos, daher gibt es einen Leistungsverlust.

Doug Lubey aus Louisiana
quelle
1
Wenn Sie Ihren Servernamen nicht kennen, verwenden Sie SELECT @@SERVERNAME. Sie können auchEXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
Contango
24

Wenn Sie das Glück haben, SQL 2012 oder höher zu haben, können Sie verwenden dm_exec_describe_first_result_set_for_object

Ich habe gerade die von gotqn bereitgestellte SQL bearbeitet. Danke gotqn.

Dadurch wird eine globale temporäre Tabelle mit dem gleichen Namen wie der Prozedurname erstellt. Die temporäre Tabelle kann später nach Bedarf verwendet werden. Vergessen Sie nur nicht, es fallen zu lassen, bevor Sie es erneut ausführen.

    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
quelle
1
Ausgezeichnet! Nur eine Bemerkung: Verwenden Sie sys.all_objectsanstelle von, sys.procedureswenn Sie dies für integrierte gespeicherte Prozeduren tun möchten.
Gert Arnold
2
Dies schlägt auch fehl, wenn der SP temporäre Tabellen verwendet. (aber es ist ziemlich praktisch, dies als
Proc
23

Dieser gespeicherte Prozess erledigt den Job:

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

Dies ist eine leichte Überarbeitung: Fügen Sie die Ergebnisse gespeicherter Prozeduren in die Tabelle ein, damit sie tatsächlich funktionieren.

Wenn Sie möchten, dass es mit einer temporären Tabelle funktioniert, müssen Sie eine ##GLOBALTabelle verwenden und anschließend löschen.

Satnhak
quelle
17

Um den ersten Datensatz einer gespeicherten Prozedur in eine temporäre Tabelle einzufügen, müssen Sie Folgendes wissen:

  1. Nur der erste Zeilensatz der gespeicherten Prozedur kann in eine temporäre Tabelle eingefügt werden
  2. Die gespeicherte Prozedur darf keine dynamische T-SQL-Anweisung ausführen ( sp_executesql)
  3. Sie müssen zuerst die Struktur der temporären Tabelle definieren

Das Obige mag als Einschränkung erscheinen, aber meiner Meinung nach ist es vollkommen sinnvoll - wenn Sie verwenden sp_executesql, können Sie einmal zwei Spalten und einmal zehn zurückgeben, und wenn Sie mehrere Ergebnismengen haben, können Sie diese nicht auch in mehrere Tabellen einfügen - Sie können maximal einfügen in zwei Tabellen in einer T-SQL-Anweisung (mit OUTPUTKlausel und ohne Trigger).

Daher geht es hauptsächlich darum, wie die temporäre Tabellenstruktur definiert wird, bevor die EXEC ... INTO ...Anweisung ausgeführt wird.

Der erste funktioniert mit, OBJECT_IDwährend der zweite und der dritte auch mit Ad-hoc-Abfragen arbeiten. Ich bevorzuge die Verwendung der DMV anstelle der sp, da Sie CROSS APPLYdie temporären Tabellendefinitionen für mehrere Prozeduren gleichzeitig verwenden und erstellen können.

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;

Achten Sie auch auf das system_type_nameFeld, da es sehr nützlich sein kann. Es speichert die vollständige Spaltendefinition. Zum Beispiel:

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

In den meisten Fällen können Sie die Tabellendefinition direkt verwenden.

Daher denke ich, dass Sie in den meisten Fällen (wenn die gespeicherte Prozedur bestimmten Kriterien entspricht) problemlos dynamische Anweisungen zur Lösung solcher Probleme erstellen können (erstellen Sie die temporäre Tabelle, fügen Sie das Ergebnis der gespeicherten Prozedur ein, tun Sie, was Sie mit den Daten benötigen). .


Beachten Sie, dass die oben genannten Objekte in einigen Fällen die Daten der ersten Ergebnismenge nicht definieren können, z. B. wenn dynamische T-SQL-Anweisungen ausgeführt werden oder temporäre Tabellen in der gespeicherten Prozedur verwendet werden.

gotqn
quelle
Praktische Beobachtung der Einschränkungen: Wenn Sie die Ausgabe eines SP (nennen wir es SP_LEVEL_0) in eine temporäre Tabelle einfügen müssen, die dynamisch mit dem obigen Ansatz in einem anderen SP erstellt wurde (nennen wir es SP_LEVEL_1), können Sie nicht denselben Trick für die Ausgabe dieses SP_LEVEL_1 an ausführen einige andere temporäre Tabelle in SP_LEVEL_2
nahab
17
  1. Ich erstelle eine Tabelle mit dem folgenden Schema und den folgenden Daten.
  2. Erstellen Sie eine gespeicherte Prozedur.
  3. Jetzt weiß ich, was das Ergebnis meiner Prozedur ist, und führe die folgende Abfrage durch.

    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] On

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

Wenn die Abfrage keine Parameter enthält, verwenden Sie OpenQueryelse use OpenRowset.

Grundlegend wäre, ein Schema gemäß der gespeicherten Prozedur zu erstellen und in diese Tabelle einzufügen. z.B:

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
Problemlöser
quelle
13

Code

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

Ich hoffe das hilft. Bitte qualifizieren Sie sich entsprechend.

SoftwareARM
quelle
11

Ich habe festgestellt, dass Arrays / DataTables an gespeicherte Prozeduren übergeben werden die Ihnen möglicherweise eine weitere Vorstellung davon geben, wie Sie Ihr Problem lösen können.

Der Link schlägt vor, einen Bildtypparameter zu verwenden, um an die gespeicherte Prozedur zu übergeben. In der gespeicherten Prozedur wird das Bild dann in eine Tabellenvariable umgewandelt, die die Originaldaten enthält.

Vielleicht gibt es eine Möglichkeit, dies mit einer temporären Tabelle zu verwenden.

Kevchadders
quelle
4
Dies ist in den Versionen Sql2008 und höher mit der Einführung von Tabellenwertparametern nicht mehr erforderlich . Jetzt können Sie ein .net-Dataset oder ein datierbares Objekt direkt an eine gespeicherte SQL-Prozedur übergeben, wobei Sie die Konvertierung in Byte durchführen müssen, wie im obigen Link erwähnt
EndlessSpace
10

Ich bin auf das gleiche Problem gestoßen, und hier ist, was ich dafür aus Pauls Vorschlag heraus getan habe . Der Hauptteil ist hier zu verwenden NEWID(), um zu vermeiden, dass mehrere Benutzer die Speicherprozeduren / Skripte gleichzeitig ausführen, der Schmerz für globale temporäre Tabelle.

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
quelle
9

Eine andere Methode besteht darin, einen Typ zu erstellen und mit PIPELINED Ihr Objekt zurückzugeben. Dies beschränkt sich jedoch darauf, die Spalten zu kennen. Aber es hat den Vorteil, in der Lage zu sein:

SELECT * 
FROM TABLE(CAST(f$my_functions('8028767') AS my_tab_type))
Pierre
quelle
Was ist das? Scheint nichts mit SQL Server zu tun zu haben, um das es in dieser Frage geht
Martin Smith
8

Es ist ein einfacher Prozess in zwei Schritten: - Erstellen einer temporären Tabelle - Einfügen in die temporäre Tabelle.

Code, um dasselbe auszuführen:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;
S Krishna
quelle
Abgestimmt; sehr ähnlich zu bestehenden Antworten.
Iokevins
6

Nachdem ich mich umgesehen hatte, fand ich eine Möglichkeit, eine temporäre Tabelle dynamisch für jede gespeicherte Prozedur zu erstellen, ohne ein generisches Schema der Ergebnisdefinition der gespeicherten Prozedur zu verwenden OPENROWSEToder zu OPENQUERYverwenden, insbesondere wenn Sie kein Datenbankadministrator sind.

Der SQL Server verfügt über einen integrierten Prozess sp_describe_first_result_set, der Ihnen ein Schema für jede Prozedur-Ergebnismenge bereitstellen kann. Ich habe aus den Ergebnissen dieser Prozedur eine Schematabelle erstellt und das gesamte Feld manuell auf NULLABLE gesetzt.

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 

Entwickelt und getestet auf SQL Server Version - Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (Build 17134 :)

Sie können das Schema für Ihre verwendete SQL Server-Version anpassen (falls erforderlich).

vendettamit
quelle
4

Wenn Sie die Parameter kennen, die übergeben werden, und wenn Sie keinen Zugriff auf sp_configure haben, bearbeiten Sie die gespeicherte Prozedur mit diesen Parametern. Diese können in einer globalen Tabelle ## gespeichert werden.

lakshmivisalij
quelle
3

Dies ist in SQL Server 2014+ möglich, sofern die gespeicherte Prozedur nur eine Tabelle zurückgibt. Wenn jemand einen Weg findet, dies für mehrere Tische zu tun, würde ich gerne davon erfahren.

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

Dadurch wird die Definition der zurückgegebenen Tabelle aus den Systemtabellen abgerufen und damit die temporäre Tabelle für Sie erstellt. Sie können es dann wie zuvor angegeben aus der gespeicherten Prozedur füllen.

Es gibt auch Varianten davon, die auch mit Dynamic SQL funktionieren.

Matthew Baker
quelle
2

Ein paar Jahre zu spät zur Frage, aber ich brauchte so etwas für eine schnelle und schmutzige Codegenerierung. Ich glaube, wie andere gesagt haben, ist es einfacher, die temporäre Tabelle im Voraus zu definieren, aber diese Methode sollte für einfache Abfragen gespeicherter Prozeduren oder SQL-Anweisungen funktionieren.

Dies wird ein wenig kompliziert sein, aber es leiht sich von den Mitwirkenden hier sowie von Paul Whites Lösung von DBA Stack Exchange Get gespeicherte Prozedur-Ergebnisspaltentypen aus . Um diesen Ansatz zu wiederholen, ist das Beispiel nicht für Prozesse in einer Mehrbenutzerumgebung konzipiert. In diesem Fall wird die Tabellendefinition für eine kurze Zeit in einer globalen temporären Tabelle als Referenz für einen Codegenerierungsvorlagenprozess festgelegt.

Ich habe dies noch nicht vollständig getestet, daher kann es zu Einschränkungen kommen, sodass Sie möglicherweise auf den MSDN-Link in Paul Whites Antwort zugreifen möchten. Dies gilt für SQL 2012 und höher.

Verwenden Sie zuerst die gespeicherte Prozedur sp_describe_first_result_set, die der Beschreibung von Oracle ähnelt.

Dadurch wird die erste Zeile der ersten Ergebnismenge ausgewertet. Wenn Ihre gespeicherte Prozedur oder Anweisung mehrere Abfragen zurückgibt, wird nur das erste Ergebnis beschrieben.

Ich habe einen gespeicherten Prozess erstellt, um die Aufgaben aufzuschlüsseln, die ein einzelnes Feld zur Auswahl zurückgeben, um die temporäre Tabellendefinition zu erstellen.

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

Das Rätsel ist, dass Sie eine globale Tabelle verwenden müssen, diese jedoch so eindeutig machen müssen, dass Sie sie häufig löschen und erstellen können, ohne sich um eine Kollision sorgen zu müssen.
Im Beispiel habe ich eine Guid (FE264BF5_9C32_438F_8462_8A5DC8DEE49E) für die globale Variable verwendet, die die Bindestriche durch Unterstriche ersetzt

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

Auch hier habe ich es nur mit einfachen Abfragen für gespeicherte Prozeduren und einfachen Abfragen getestet, sodass Ihr Kilometerstand variieren kann. Hoffe das hilft jemandem.

Charles Byrne
quelle
1

Nun, Sie müssen eine temporäre Tabelle erstellen, aber sie muss nicht das richtige Schema haben. Ich habe eine gespeicherte Prozedur erstellt, die eine vorhandene temporäre Tabelle so ändert, dass sie die erforderlichen Spalten mit den richtigen Daten enthält Typ und Reihenfolge (Löschen aller vorhandenen Spalten, Hinzufügen neuer Spalten):

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

Beachten Sie, dass dies nicht funktioniert, wenn sys.dm_exec_describe_first_result_set_for_object die Ergebnisse der gespeicherten Prozedur nicht ermitteln kann (z. B. wenn eine temporäre Tabelle verwendet wird).

jmoreno
quelle
0

Wenn Sie Dynamic SQL eine temporäre Tabelle erstellen lassen, gehört diese Tabelle der Dynamic SQL-Verbindung, im Gegensatz zu der Verbindung, von der aus Ihre gespeicherte Prozedur aufgerufen wird.

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;

Geben Sie hier die Bildbeschreibung ein

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;

Meldung 208, Ebene 16, Status 0 Ungültiger Objektname '#Pivoted'. Dies liegt daran, dass #Pivoted der Dynamic SQL-Verbindung gehört. Also die letzte Anweisung

SELECT * FROM #Pivoted

schlägt fehl.

Eine Möglichkeit, dieses Problem nicht zu lösen, besteht darin, sicherzustellen, dass alle Verweise auf #Pivoted aus der dynamischen Abfrage selbst stammen:

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);

Geben Sie hier die Bildbeschreibung ein

Ludovic Aubert
quelle
-5

Ich würde folgendes tun

  1. Erstellen Sie eine UDF (konvertieren Sie SP in) (Tabellenwert UDF).

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

Hlin
quelle
2
Es könnte einige Hindernisse geben, um Ihren ersten Schritt zu machen. Zum Beispiel, wenn der ursprüngliche SP temporäre Tabellen verwendet. UDFs können keine temporären Tabellen verwenden.
Yucer