it-swarm.com.de

T-SQL Dynamic SQL- und Temp-Tabellen

Es sieht so aus, als ob #temptables, die mit dynamischem SQL mithilfe der EXECUTE-Zeichenfolgenmethode erstellt wurden, einen anderen Gültigkeitsbereich haben und nicht von "festen" SQLs in derselben gespeicherten Prozedur referenziert werden können. Ich kann jedoch auf eine temporäre Tabelle verweisen, die von einer dynamischen SQL-Anweisung in einer dynamischen SQL-Untersequenz erstellt wurde. Es scheint jedoch, dass eine gespeicherte Prozedur kein Abfrageergebnis an einen aufrufenden Client zurückgibt, es sei denn, die SQL ist behoben.

Ein einfaches Szenario mit 2 Tabellen: Ich habe 2 Tabellen. Nennen wir sie Bestellungen und Artikel. Die Bestellung hat einen Primärschlüssel mit der Bestellnummer und die Artikel haben einen Primärschlüssel mit der Bestellnummer. Items.OrderId ist der Fremdschlüssel zur Identifizierung der übergeordneten Bestellung. Eine Bestellung kann 1 bis n Artikel enthalten.

Ich möchte in der Lage sein, dem Benutzer eine sehr flexible Oberfläche vom Typ "Abfrageerstellung" zur Verfügung zu stellen, damit der Benutzer auswählen kann, welche Elemente er sehen möchte. Die Filterkriterien können auf Feldern aus der Tabelle Artikel und/oder aus der übergeordneten Tabelle Bestellung basieren. Wenn ein Artikel die Filterbedingung einschließlich und die Bedingung für die übergeordnete Bestellung erfüllt, sollte der Artikel in der Abfrage sowie in der übergeordneten Bestellung zurückgegeben werden.

Normalerweise würden die meisten Leute eine Verknüpfung zwischen der Item-Tabelle und den übergeordneten Order-Tabellen erstellen. Ich möchte stattdessen 2 separate Abfragen durchführen. Eine, um alle qualifizierenden Artikel zurückzugeben, und die andere, um alle unterschiedlichen übergeordneten Bestellungen zurückzugeben. Der Grund ist zweifach und Sie können oder können nicht zustimmen.

Der erste Grund ist, dass ich alle Spalten in der übergeordneten Bestellungstabelle abfragen muss. Wenn ich eine einzelne Abfrage ausführen würde, um die Bestellungstabelle mit der Artikeltabelle zu verknüpfen, würde ich die Bestellinformationen mehrmals wiederholen. Da es in der Regel eine große Anzahl von Artikeln pro Bestellung gibt, möchte ich dies vermeiden, da dies dazu führen würde, dass viel mehr Daten an einen Fat Client übertragen werden. Stattdessen möchte ich, wie bereits erwähnt, die beiden Tabellen einzeln in einem Dataset zurückgeben und die beiden Tabellen darin verwenden, um ein benutzerdefiniertes Order- und ein untergeordnetes Items-Clientobjekt aufzufüllen. (Ich weiß noch nicht genug über LINQ oder Entity Framework. Ich baue meine Objekte von Hand). Der zweite Grund, warum ich zwei Tabellen anstelle von einer zurückgeben möchte, ist, dass ich bereits eine andere Prozedur habe, die alle Elemente für eine bestimmte OrderId zusammen mit der übergeordneten Order zurückgibt, und dass ich denselben 2-Tabellen-Ansatz verwenden möchte, damit ich könnte den Client-Code wiederverwenden, um meine benutzerdefinierten Order- und Client-Objekte aus den 2 zurückgegebenen Datentabellen zu füllen.

Was ich mir erhofft hatte, war folgendes:

Erstellen Sie auf dem Client eine dynamische SQL-Zeichenfolge, die die Tabelle "orders" mit der Tabelle "Items" verknüpft und die entsprechenden Filter für jede Tabelle gemäß dem benutzerdefinierten Filter, der in der Winform-Fat-Client-App erstellt wurde. Der SQL-Build auf dem Client hätte ungefähr so ​​ausgesehen:

TempSQL = "

    INSERT INTO #ItemsToQuery
       OrderId, ItemsId
    FROM
       Orders, Items 
    WHERE
       Orders.OrderID = Items.OrderId AND
       /* Some unpredictable Order filters go here */
      AND
       /* Some unpredictable Items filters go here */
    "

Dann würde ich eine gespeicherte Prozedur aufrufen,

CREATE PROCEDURE GetItemsAndOrders(@tempSql as text)
   Execute (@tempSQL) --to create the #ItemsToQuery table

SELECT * FROM Items WHERE Items.ItemId IN (SELECT ItemId FROM #ItemsToQuery)

SELECT * FROM Orders WHERE Orders.OrderId IN (SELECT DISTINCT OrderId FROM #ItemsToQuery)

Das Problem bei diesem Ansatz besteht darin, dass auf die Tabelle #ItemsToQuery, da sie mit dynamischem SQL erstellt wurde, in den folgenden zwei statischen SQLs nicht zugegriffen werden kann. Wenn ich die statischen SQLs in dynamisch ändere, werden keine Ergebnisse an den Fat Client zurückgegeben.

3 in den Sinn kommen, aber ich bin auf der Suche nach einem besseren:

1) Der erste SQL-Befehl kann ausgeführt werden, indem der dynamisch erstellte SQL-Befehl vom Client ausgeführt wird. Die Ergebnisse könnten dann als Tabelle an eine modifizierte Version der oben gespeicherten Prozedur übergeben werden. Ich bin mit der Übergabe von Tabellendaten als XML vertraut. In diesem Fall könnte der gespeicherte Prozess die Daten mit statischem SQL in eine temporäre Tabelle einfügen, die dann ohne Probleme abgefragt werden kann, da sie mit dynamischem SQL erstellt wurden. (Ich könnte auch die Übergabe des neuen Tabellentyp-Parameters anstelle von XML untersuchen.) Ich möchte jedoch vermeiden, dass potenziell große Listen an eine gespeicherte Prozedur übergeben werden.

2) Ich konnte alle Abfragen vom Client ausführen.

Das erste wäre etwa so:

SELECT Items.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)
SELECT Orders.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)

Dies gibt mir weiterhin die Möglichkeit, den clientseitigen Objektpopulationscode wiederzuverwenden, da die Bestellungen und Artikel weiterhin in zwei verschiedenen Tabellen zurückgegeben werden.

Ich habe das Gefühl, dass ich einige Optionen haben könnte, die einen Tabellendatentyp in meinem gespeicherten Prozess verwenden, aber das ist auch neu für mich und ich würde es begrüßen, wenn ich ein bisschen Löffel darauf füttere.

Wenn Sie in dem, was ich geschrieben habe, sogar so weit gescannt haben, bin ich überrascht, aber wenn ja, würde ich Ihre Gedanken darüber, wie dies am besten erreicht werden kann, unterschätzen.

14
ChadD

Sie müssen zuerst eine Tabelle erstellen, damit diese in der dynamischen SQL verfügbar ist.

Das funktioniert:

CREATE TABLE #temp3 (id INT)
EXEC ('insert #temp3 values(1)')

SELECT *
FROM #temp3

Das wird nicht funktionieren:

EXEC (
        'create table #temp2 (id int)
         insert #temp2 values(1)'
        )

SELECT *
FROM #temp2

Mit anderen Worten:

  1. Temporäre Tabelle erstellen
  2. Proz. Ausführen
  3. Wählen Sie aus der Temp-Tabelle

Hier ist ein vollständiges Beispiel:

CREATE PROC prTest2 @var VARCHAR(100)
AS
EXEC (@var)
GO

CREATE TABLE #temp (id INT)

EXEC prTest2 'insert #temp values(1)'

SELECT *
FROM #temp
20
SQLMenace

1. Methode - Schließen Sie mehrere Anweisungen in denselben Dynamic SQL-Aufruf ein:

DECLARE @DynamicQuery NVARCHAR(MAX)

SET @DynamicQuery = 'Select * into #temp from (select * from tablename) alias 
select * from #temp
drop table #temp'

EXEC sp_executesql @DynamicQuery

2. Methode - Globale Temp-Tabelle verwenden:
(Vorsicht, Sie müssen die globale Variable besonders beachten.)

IF OBJECT_ID('tempdb..##temp2') IS NULL
BEGIN
    EXEC (
            'create table ##temp2 (id int)
             insert ##temp2 values(1)'
            )

    SELECT *
    FROM ##temp2
END

Vergessen Sie nicht, das ## temp2-Objekt manuell zu löschen, sobald Sie damit fertig sind:

IF (OBJECT_ID('tempdb..##temp2') IS NOT NULL)
BEGIN
     DROP Table ##temp2
END

Hinweis: Verwenden Sie diese Methode 2 nicht, wenn Sie die vollständige Struktur der Datenbank nicht kennen.

4
Sid

Ich empfehle dringend, dass Sie sich http://www.sommarskog.se/arrays-in-sql-2005.html durchlesen.

Persönlich mag ich den Ansatz, eine durch Kommas getrennte Textliste zu übergeben, sie dann mit der Text-zu-Tabellen-Funktion zu analysieren und sich ihr anzuschließen. Der temporäre Tabellenansatz kann funktionieren, wenn Sie ihn zuerst in der Verbindung erstellen. Aber es fühlt sich etwas unordentlicher an.

2
Sam Saffron

Ich hatte das gleiche Problem, das @Muflix erwähnte. Wenn Sie nicht wissen, welche Spalten zurückgegeben werden, oder wenn sie dynamisch generiert werden, habe ich eine globale Tabelle mit einer eindeutigen ID erstellt und diese gelöscht, wenn ich damit fertig bin. Das sieht ungefähr so ​​aus, wie es angezeigt wird unten:

DECLARE @DynamicSQL NVARCHAR(MAX)
DECLARE @DynamicTable VARCHAR(255) = 'DynamicTempTable_' + CONVERT(VARCHAR(36), NEWID())
DECLARE @DynamicColumns NVARCHAR(MAX)

--Get "@DynamicColumns", example: SET @DynamicColumns = '[Column1], [Column2]'

SET @DynamicSQL = 'SELECT ' + @DynamicColumns + ' INTO [##' + @DynamicTable + ']' + 
     ' FROM [dbo].[TableXYZ]'

EXEC sp_executesql @DynamicSQL

SET @DynamicSQL = 'IF OBJECT_ID(''tempdb..##' + @DynamicTable + ''' , ''U'') IS NOT NULL ' + 
    ' BEGIN DROP TABLE [##' + @DynamicTable + '] END'

EXEC sp_executesql @DynamicSQL

Mit Sicherheit nicht die beste Lösung, aber das scheint bei mir zu funktionieren.

1
David Rogers

Ergebnismengen aus dynamischem SQL werden an den Client zurückgegeben. Ich habe das ziemlich oft gemacht.

Sie haben Recht mit Problemen beim Teilen von Daten über temporäre Tabellen und Variablen und ähnlichen Dingen zwischen dem SQL und dem von ihm generierten dynamischen SQL.

Ich denke, wenn Sie versuchen, Ihre temporäre Tabelle zum Laufen zu bringen, haben Sie wahrscheinlich einige Dinge durcheinander gebracht, weil Sie definitiv Daten von einem SP erhalten können, der dynamisches SQL ausführt:

USE SandBox
GO

CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS 
BEGIN
    DECLARE @sql AS VARCHAR(MAX) = 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + ''''
    EXEC (@sql)
END
GO

EXEC usp_DynTest 'BASE TABLE'
GO

EXEC usp_DynTest 'VIEW'
GO

DROP PROCEDURE usp_DynTest
GO

Ebenfalls:

USE SandBox
GO

CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS 
BEGIN
    DECLARE @sql AS VARCHAR(MAX) = 'SELECT * INTO #temp FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + '''; SELECT * FROM #temp;'
    EXEC (@sql)
END
GO

EXEC usp_DynTest 'BASE TABLE'
GO

EXEC usp_DynTest 'VIEW'
GO

DROP PROCEDURE usp_DynTest
GO
0
Cade Roux