sql >> Databáze >  >> RDS >> Sqlserver

Výsledek tabulky serveru SQL do pole v SQL Server 2005

Ve skutečnosti to všechno můžete udělat v jednom výběrovém dotazu CTE bez použití jakýchkoli funkcí. Zde je postup:

Nejprve zvažte tuto strukturu rodičovské/podřízené tabulky:

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

(Použil jsem P a C, abych ušetřil psaní!)

A pojďme přidat nějaká testovací data, která odpovídají datům tazatele:

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

Pak konečně výraz CTE:

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

Výsledek:

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

Abych vysvětlil, co se zde děje, popíšu každou část CTE a co dělá.

FirstItems podívá se na všechny potomky a najde nejnižší ID v každé nadřazené skupině, které se použije jako kotva pro další rekurzivní SELECT:

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

SubItemList je rekurzivní SELECT, který vybere nejnižšího potomka z předchozího dotazu a každému potomkovi přidělí zvyšující se číslo položky počínaje 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
)

Problém je v tom, že mnoho položek dupává a opakuje, takže ItemList filtruje to tak, aby vybral maximum z každé skupiny:

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

Nyní máme ID seznam rodičů s každým z dětí očíslovaných od 1 do x:

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

SubArrayList vezme dětské řádky, rekurzivně se připojí k seznamu čísel a začne k sobě připojovat všechny popisy, počínaje jediným popisem:

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

Výsledek je nyní:

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

Takže vše, co musíme udělat, je zbavit se všech částečně zřetězených řádků.

MaxItems jednoduše vezme seznam rodičů a jejich nejvyšší čísla položek, což dělá následující dotaz o něco jednodušší:

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

ArrayList provede konečné vyřazení částečně zřetězených řádků pomocí maximálního čísla položky získaného z předchozího dotazu:

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

A nakonec zbývá jen dotaz na výsledek:

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


  1. Sloupec SQLAlchemy JSON - jak provést dotaz obsahuje

  2. Použití Dapper s uloženými procedurami Oracle, které vracejí kurzory

  3. T-SQL 2005:Předávání hodnot Null prostřednictvím datového typu XML

  4. Upozornění:Dlouhé semaforské čekání