Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL Server-Tabellenergebnis in Array in SQL Server 2005

Sie können dies tatsächlich alles in einer CTE-Auswahlabfrage tun, ohne irgendwelche Funktionen zu verwenden. So geht's:

Betrachten Sie zunächst diese übergeordnete/untergeordnete Tabellenstruktur:

CREATE TABLE P (ID INT PRIMARY KEY, Description VARCHAR(20));
CREATE TABLE C (ID INT PRIMARY KEY, PID INT, 
                Description VARCHAR(20), 
                CONSTRAINT fk FOREIGN KEY (PID) REFERENCES P(ID));

(Ich habe P und C verwendet, um Tipparbeit zu sparen!)

Und fügen wir einige Testdaten hinzu, die denen des Fragestellers entsprechen:

INSERT INTO P VALUES (36, 'Blah Blah');
INSERT INTO P VALUES (20, 'Pah Pah');

INSERT INTO C VALUES (1, 36, 'Bob');
INSERT INTO C VALUES (2, 36, 'Gary');
INSERT INTO C VALUES (3, 36, 'Reginald');
INSERT INTO C VALUES (4, 20, 'Emily');
INSERT INTO C VALUES (5, 20, 'Dave');

Dann endlich der CTE-Ausdruck:

WITH
FirstItems (PID, FirstCID) AS (    

    SELECT C.PID, MIN(C.ID)
      FROM C
     GROUP BY C.PID      
),  
SubItemList (PID, CID, ItemNum) AS (

    SELECT C.PID, C.ID, 1
      FROM C JOIN FirstItems FI ON (C.ID = FI.FirstCID)
    UNION ALL
    SELECT C.PID, C.ID, IL.ItemNum + 1
      FROM C JOIN SubItemList IL ON C.PID = IL.PID AND C.ID > CID
),
ItemList (PID, CID, ItemNum) AS (

    SELECT PID, CID, MAX(ItemNum)
      FROM SubItemList
     GROUP BY PID, CID
),
SubArrayList (PID, CID, Array, ItemNum) AS (

    SELECT IL.PID, IL.CID, CAST(C.Description AS VARCHAR(MAX)), IL.ItemNum
      FROM ItemList IL JOIN C ON IL.CID = C.ID
     WHERE IL.ItemNum = 1
    UNION ALL
    SELECT IL.PID, IL.CID, AL.Array + ',' + CAST(C.Description AS VARCHAR(MAX)), IL.ItemNum
      FROM ItemList IL
      JOIN SubArrayList AL ON (IL.PID = AL.PID AND IL.ItemNum = AL.ItemNum + 1)
      JOIN C ON (IL.CID = C.ID)
),
MaxItems (PID, MaxItem) AS (

    SELECT PID, MAX(ItemNum)
      FROM SubItemList
     GROUP BY PID

),
ArrayList (PID, List) AS (

    SELECT SAL.PID, SAL.Array
      FROM SubArrayList SAL 
      JOIN MaxItems MI ON (SAL.PID = MI.PID AND SAL.ItemNum = MI.MaxItem)

)
SELECT P.ID, P.Description, AL.List
  FROM ArrayList AL JOIN P ON P.ID = AL.PID
 ORDER BY P.ID

Ergebnis:

ID Description    List
-- -------------- --------
20 Pah Pah        Emily,Dave
36 Blah Blah      Bob,Gary,Reginald   

Um zu erklären, was hier vor sich geht, beschreibe ich jeden Teil des CTE und was er tut.

ErsteElemente schaut sich alle Kinder an und findet die niedrigste ID in jeder Elterngruppe, um sie als Anker für das nächste rekursive SELECT zu verwenden:

FirstItems (PID, FirstCID) AS (
    SELECT C.PID, MIN(C.ID)
      FROM C
     GROUP BY C.PID  
)

SubItemList ist ein rekursives SELECT, das das niedrigste Kind aus der vorherigen Abfrage aufnimmt und jedem Kind eine aufsteigende Elementnummer zuweist, beginnend bei 1:

SubItemList (PID, CID, ItemNum) AS (    
    SELECT C.PID, C.ID, 1
      FROM C JOIN FirstItems FI ON (C.ID = FI.FirstCID)
    UNION ALL
    SELECT C.PID, C.ID, IL.ItemNum + 1
      FROM C JOIN SubItemList IL ON C.PID = IL.PID AND C.ID > CID
)

Das Problem ist, dass es viele Elemente dupliziert und wiederholt, also ItemList filtert es, um nur das Maximum aus jeder Gruppe auszuwählen:

ItemList (PID, CID, ItemNum) AS (
SELECT PID, CID, MAX(ItemNum)
  FROM SubItemList
 GROUP BY PID, CID
)

Jetzt haben wir eine ID-Liste der Eltern, in der jedes Kind von 1 bis x nummeriert ist:

PID         CID         ItemNum
----------- ----------- -----------
36          1           1
36          2           2
36          3           3
20          4           1
20          5           2

SubArrayList nimmt die untergeordneten Zeilen, verbindet sich rekursiv mit der Zahlenliste und beginnt, alle Beschreibungen aneinander anzuhängen, beginnend mit einer einzigen Beschreibung:

SubArrayList (PID, CID, Array, ItemNum) AS (    
    SELECT IL.PID, IL.CID, CAST(C.Description AS VARCHAR(MAX)), IL.ItemNum
      FROM ItemList IL JOIN C ON IL.CID = C.ID
     WHERE IL.ItemNum = 1
    UNION ALL
    SELECT IL.PID, IL.CID, AL.Array + ',' + CAST(C.Description AS VARCHAR(MAX)), IL.ItemNum
      FROM ItemList IL
      JOIN SubArrayList AL ON (IL.PID = AL.PID AND IL.ItemNum = AL.ItemNum + 1)
      JOIN C ON (IL.CID = C.ID)
)

Das Ergebnis ist nun:

PID         CID         Array             ItemNum
----------- ----------- ----------------- -----------
36          1           Bob               1
20          4           Emily             1
20          5           Emily,Dave        2
36          2           Bob,Gary          2
36          3           Bob,Gary,Reginald 3

Alles, was wir tun müssen, ist, alle teilweise verketteten Zeilen loszuwerden.

MaxItems schnappt sich einfach eine Liste der Eltern und ihrer höchsten Artikelnummern, was die folgende Abfrage etwas einfacher macht:

MaxItems (PID, MaxItem) AS (    
    SELECT PID, MAX(ItemNum)
      FROM SubItemList
     GROUP BY PID        
)

ArrayList führt die endgültige Aussortierung der teilweise verketteten Zeilen unter Verwendung der maximalen Elementnummer durch, die aus der vorherigen Abfrage abgerufen wurde:

ArrayList (PID, List) AS (
SELECT SAL.PID, SAL.Array
  FROM SubArrayList SAL 
  JOIN MaxItems MI ON (SAL.PID = MI.PID AND SAL.ItemNum = MI.MaxItem)     
)

Und zum Schluss muss nur noch das Ergebnis abgefragt werden:

SELECT P.ID, P.Description, AL.List
  FROM ArrayList AL JOIN P ON P.ID = AL.PID
 ORDER BY P.ID