sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server-tabellresultat till array i SQL Server 2005

Du kan faktiskt göra allt detta i en CTE-valfråga, utan att använda några funktioner. Så här gör du:

Tänk först på den här överordnade/underordnade tabellstrukturen:

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

(Jag har använt P och C för att spara på att skriva!)

Och låt oss lägga till lite testdata som matchar frågeställarens:

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

Till sist, CTE-uttrycket:

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

Resultat:

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

För att förklara vad som händer här kommer jag att beskriva varje del av CTE och vad den gör.

FirstItems tittar på alla barn och hittar det lägsta ID i varje föräldragrupp att använda som ankare för nästa rekursiva SELECT:

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

SubItem List är en rekursiv SELECT som plockar upp det lägsta barnet från den föregående frågan och tilldelar ett ökande artikelnummer till varje barn från 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
)

Problemet är att det dupar upp och upprepar många av objekten, så ItemList filtrerar den för att bara välja maxvärdet från varje grupp:

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

Nu har vi en ID-lista över föräldrar med vart och ett av deras barn numrerade från 1 till x:

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

SubArrayList tar barnraderna, ansluter sig rekursivt till nummerlistan och börjar lägga till alla beskrivningar till varandra, börjar med en enda beskrivning:

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

Resultatet är nu:

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

Så allt vi behöver göra är att bli av med alla delvis sammanlänkade rader.

MaxItems tar helt enkelt en lista över föräldrar och deras högsta artikelnummer, vilket gör följande fråga lite enklare:

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

ArrayList utför den sista urtagningen av de delvis sammanlänkade raderna med det maximala artikelnumret som hämtats från föregående fråga:

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

Och slutligen, allt som återstår är att fråga efter resultatet:

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


  1. Hur man ställer in automatisk failover för Moodle MySQL-databasen

  2. Hur kan jag söka efter rader runt ett givet strängvärde?

  3. Anropar lagrad procedur med VBA

  4. Visar BLOB-bild från Mysql-databas till dynamisk div i html