SQL server Wie man ein Tabellenergebnis aus mehreren Ergebnissen mit einer WHILE-Abfrage ausgibt

Von dieser Antwort: Gibt es eine Möglichkeit, durch eine Tabellenvariable in TSQL ohne einen Cursor zu schleifen?

Ich benutze die Methode

WHILE EXISTS(SELECT * FROM #Temp) 

Das Problem ist, dass es die Ausgabe mehrerer Tabellen, wenn möglich, würde ich gerne als eine einzelne Tabelle ausgeben.

 Declare @Id int WHILE EXISTS(SELECT * FROM #Temp) Begin Select Top 1 @Id = Id From #Temp --Do some processing here Delete #Temp Where Id = @Id End 

Also jetzt gibt es das aus:

 xy -- -- 1 a xy -- -- 1 b 

Aber ich möchte, dass es das ausgibt:

 xy -- -- 1 a 2 b 

Was ich zu erreichen versuche , habe ich das in einem Feld:

1234,1432,1235

Ich habe einen process, der das Feld in datasätze aufteilt (es funktioniert mit SQL server 2000):

 DECLARE @String VARCHAR(100) SELECT @String = str FROM field --with the 1234,1432,1235 SELECT SUBSTRING(',' + @String + ',', Number + 1, CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1)AS str INTO #temp FROM master..spt_values WHERE Type = 'P' AND Number <= LEN(',' + @String + ',') - 1 AND SUBSTRING(',' + @String + ',', Number, 1) = ',' GO 

Also jetzt, #temp hat:

 str --- 1234 1432 1235 str str --- 1234 1432 1235 --- str --- 1234 1432 1235 1234 str --- 1234 1432 1235 

Also muss ich jeden datasatz durchlaufen, um die benötigten Informationen abzufragen.

Und ich möchte, dass es so etwas ausgibt:

 str name age --- ---- --- 1234 Bob 23 1432 Jay 41 1235 Tim 12 

Die aktuelle while-loop gibt es so aus, was ich nicht will:

 str name age --- ---- --- 1234 Bob 23 str name age --- ---- --- 1432 Jay 41 str name age --- ---- --- 1235 Tim 12 

Endgültiges Arbeitsergebnis:

 SET NOCOUNT ON; DECLARE @String VARCHAR(1000); SELECT @String = Tnn FROM (SELECT CO.USER_2 AS Tnn FROM [VMFG].[dbo].[CUSTOMER_ORDER] AS CO LEFT JOIN DBO.Tnn_Header AS Tnn ON Tnn.TnnNumber = CO.USER_2 AND Tnn.StatusID = '5' WHERE CO.ID = 'ORDERID') AS Place --with the 1234,1432,1235 DECLARE @Id nvarchar(50), @Discount nvarchar(50), @Spin nvarchar(50), @Commission_Hmm nvarchar(50), @Commission nvarchar(50), @TnnID nvarchar(50); DECLARE @Output TABLE ( TnnNumber nvarchar(50), Discount nvarchar(50), Spin nvarchar(50), Commission_Hmm nvarchar(50), Commission nvarchar(50), TnnID nvarchar(50)); DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY FOR SELECT SUBSTRING(',' + @String + ',', Number + 1, CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1) AS [ID] FROM master..spt_values WHERE Type = 'P' AND Number <= LEN(',' + @String + ',') - 1 AND SUBSTRING(',' + @String + ',', Number, 1) = ','; OPEN crs; FETCH NEXT FROM crs INTO @Id; WHILE (@@FETCH_STATUS = 0) BEGIN -- do some processing.. SELECT @Id = TH.TnnNumber, @Discount = CASE WHEN COUNT(DISTINCT TL.DiscountCodeID) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(DC.Value) AS VARCHAR(60)) END, @Spin = CASE WHEN TS.SpinID > 4 THEN 'Has Specifics, View Tnn' ELSE TS.Value END, @Commission_Hmm = CASE WHEN COUNT(DISTINCT TL.Commission_Hmm) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX( ISNULL(str(TL.Commission_Hmm,12),'Default Comm')) AS VARCHAR(60)) END, @Commission = CASE WHEN COUNT(DISTINCT TL.Commission) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(ISNULL(str(TL.Commission,12),'Default Comm')) AS VARCHAR(60)) END, @TnnID = TL.TnnID FROM DBO.Tnn_Header AS TH LEFT JOIN DBO.Tnn_LINE AS TL ON TH.TnnID = TL.TnnID LEFT JOIN DBO.Tnn_Spin AS TS ON TH.SpinID = TS.SpinID LEFT JOIN DBO.Tnn_DiscountCode AS DC ON TL.DiscountCodeID = DC.DiscountCodeID WHERE TnnNumber = @id GROUP BY TH.TnnNumber, TS.SpinID, TS.Value, TL.TnnID -- end do some processing.. INSERT INTO @Output (TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID) VALUES (@Id, @Discount, @Spin, @Commission_Hmm, @Commission, @TnnID); FETCH NEXT FROM crs INTO @Id; END; CLOSE crs; DEALLOCATE crs; SELECT TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID FROM @Output; 

Sie verschwenden Ihre time und Energie nach solch schlechten Ratschläge. Wenn Sie unbedingt (zusätzliche Betonung auf den Muss ) einen Reihen-für-Reihe-Ansatz (CURSOR oder WHILE-loop) nehmen, dann sind Sie besser mit einem CURSOR. Es ist ein eingebautes Konstrukt, das effizienter und weniger errorsanfällig ist. Sie müssen nur die richtigen Optionen verwenden, wie READ_ONLY FORWARD_ONLY , LOCAL , READ_ONLY und FORWARD_ONLY . Sie brauchen keine STATIC wenn die Cursor-Abfrage nur temporäre Tabellen und / oder Tabellenvariablen trifft.

Die Leute werden mit diesem argumentieren und sagen, dass "du Cursors um jeden Preis vermeiden muss", aber sie haben die Tests nicht getan, um zu sehen, dass eine solche populäre Vorstellung wirklich nur ein Mythos ist. Und wenn sie Tests getan haben, die es zu bestätigen scheinen, dann haben sie nicht die entsprechenden Optionen gesetzt, meistens STATIC , die das Ergebnis der Cursor-Abfrage in eine temporäre Tabelle ablegt. Ohne diese Option wird das Abrufen neuer Zeilen die Basistabellen erneut überprüfen, um sicherzustellen, dass sie noch existieren, und das ist, wo der Performance-Treffer ist (die I / O plus die Sperre). Und das ist auch der Grund, warum Sie normalerweise nicht die STATIC Option benötigen, wenn Sie nur temporäre Tabellen und / oder Tabellenvariablen abfragen. Was verstehe ich unter "re-checking"? Schauen Sie sich die Dokumentation für @@ FETCH_STATUS an . Die Rückgabewerte decken nicht nur "Erfolg" ( 0 ) und "keine weiteren Zeilen" ( -1 ) ab: Es gibt einen Rückgabewert ( -2 ), dh "Die abgerufene Zeile fehlt".

 SET NOCOUNT ON; DECLARE @Id INT, @Name sysname, @Type VARCHAR(5); -- the Table Variable replaces #Temp2 in the original query DECLARE @Output TABLE (Id INT NOT NULL, Name sysname, [Type] VARCHAR(5)); -- the CURSOR replaces #Temp in the original query DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY FOR SELECT [object_id], name, [type] FROM sys.objects -- dbo.sysobjects for SQL 2000 -- ATable in the original query ORDER BY [object_id] ASC; OPEN crs; FETCH NEXT FROM crs INTO @Id, @Name, @Type; WHILE (@@FETCH_STATUS = 0) BEGIN INSERT INTO @Output (Id, Name, [Type]) VALUES (@Id, @Name, @Type); -- do some processing.. FETCH NEXT -- replaces the DELETE and re-SELECT in the original query FROM crs INTO @Id, @Name, @Type; END; CLOSE crs; DEALLOCATE crs; SELECT Id, Name, [Type] FROM @Output; 

AKTUALISIEREN

Wenn die Iteration über eine Abfrage erfolgt, die eine CSV von INTs aufteilt, würde die resultierende Abfrage wie folgt aussehen:

 SET NOCOUNT ON; DECLARE @String VARCHAR(1000); SELECT @String = str FROM [Table]; --with the 1234,1432,1235 DECLARE @Id INT, @Name NVARCHAR(50), @Age TINYINT; DECLARE @Output TABLE (Id INT NOT NULL, Name NVARCHAR(50), Age TINYINT); DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY FOR SELECT SUBSTRING(',' + @String + ',', Number + 1, CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1) AS [ID] FROM master..spt_values WHERE Type = 'P' AND Number <= LEN(',' + @String + ',') - 1 AND SUBSTRING(',' + @String + ',', Number, 1) = ','; OPEN crs; FETCH NEXT FROM crs INTO @Id; WHILE (@@FETCH_STATUS = 0) BEGIN -- do some processing.. -- Logic to set value of @Name -- Logic to set value of @Age INSERT INTO @Output (Id, Name, Age) VALUES (@Id, @Name, @Age); FETCH NEXT FROM crs INTO @Id; END; CLOSE crs; DEALLOCATE crs; SELECT Id, Name, Age FROM @Output; 

Ihre Frage hat Syntaxerrors, aber ich habe versucht, Abfrage und funktionierte gut

 -- this is only to populate my data table Select object_id Id, name Into #Temp From sys.tables select * into #temp2 from #Temp where 1=2 Declare @Id int WHILE EXISTS(SELECT * FROM #Temp) Begin Select Top 1 @Id = Id From #Temp ORDER BY Id -- this order is important -- use insert...into, NOT select...into insert into #temp2 select * from #Temp where Id = @Id Delete #Temp Where Id = @Id End 

BTW, du kannst nicht SELECT … INTO in eine loop haben, da die 2. Iteration Fehler auftriggers. Sie müssen # temp2 erstellen, außerhalb der loop und verwenden Sie INSERT … INTO anstelle von SELECT … INTO