2013-05-31 20 views
1

我有時會遇到有父/子關係的情況,並且想要爲每個父項選擇所有子項。您是否可以在將特定行值提取到選定列的同時進行GROUP BY

這是一個膚淺的例子。

SELECT parent.id as parent_id, child.id as child_id 
FROM parent LEFT JOIN child ON parent.id = child.parent_id 
ORDER BY parent.id 

會產生

parent_id child_id 
1   20 
1   21 
2   33 
2   67 

我想這樣做

SELECT parent.id, child1.id as child1_id, child2.id as child2_id 
FROM parent LEFT JOIN child ON parent.id = child.parent_id 
GROUP BY parent.id 

和產量

parent_id child1_id child2_id 
1   20   21 
2   33   67 

我試圖避免使用GROUP_CONCAT因爲我想創造分離每個孩子的列。

我意識到我可以兩次加入孩子並過濾選擇,但給我的實際數據集,它可能會毛茸茸的加入兩次。此外,如果您可以使用任意數量的子女來做到這一點,那真的很酷:

parent_id child1_id child2_id child3_id 
1   20   21   null 
2   33   67   109 
3   45   null  null 
+0

這是一個直接的父母和孩子之間一個層次關係或者在你想獲得孩子的所有兒童多層次的關係? –

+0

這是下一個派生關係。沒有孫子需要。 –

回答

2

你正在尋找所謂的PIVOT。 MySQL不支持PIVOT命令,但可以使用MAXCASE來模擬它。

如果您知道孩子的數量或者您是否可以擁有最大數量,這非常有用。

SELECT parent.id as parent_id, 
    MAX(CASE WHEN rn = 1 THEN child.id END) child1_id, 
    MAX(CASE WHEN rn = 2 THEN child.id END) child2_id, 
    MAX(CASE WHEN rn = 3 THEN child.id END) child3_id, 
    MAX(CASE WHEN rn = 4 THEN child.id END) child4_id, 
    MAX(CASE WHEN rn = 5 THEN child.id END) child5_id 
FROM parent 
    LEFT JOIN (
    SELECT *, 
     @rn:=IF(@prevParent=parent_id,@rn+1,1) rn, 
     @prevParent:=parent_id 
    FROM child JOIN (SELECT @rn:=0,@prevParent:=0) t 
) child ON parent.id = child.parent_id 
GROUP BY parent.id 
ORDER BY parent.id; 

你需要研究建立動態SQL,如果你不知道孩子/潛在的列數。

下面是一個例子:

SELECT 
    GROUP_CONCAT(DISTINCT 
    CONCAT(
     'MAX(IF(rn = ', rn, ',child.id,NULL)) AS child_id', rn) 
) INTO @sql 
FROM parent 
    LEFT JOIN (
    SELECT *, 
     @rn:=IF(@prevParent=parent_id,@rn+1,1) rn, 
     @prevParent:=parent_id 
    FROM child JOIN (SELECT @rn:=0,@prevParent:=0) t 
) child ON parent.id = child.parent_id 
; 

SET @sql = CONCAT('SELECT parent.id as parent_id, 
          ', @sql, ' 
        FROM parent 
        LEFT JOIN (
         SELECT *, 
         @rn:=IF(@prevParent=parent_id,@rn+1,1) rn, 
         @prevParent:=parent_id 
         FROM child JOIN (SELECT @rn:=0,@prevParent:=0) t 
       ) child ON parent.id = child.parent_id 
        GROUP BY parent.id 
        ORDER BY parent.id;'); 

PREPARE stmt FROM @sql; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 
+0

感謝您的徹底,提供小提琴。很有幫助。 –

+0

@MarkFox - np,很高興我能幫忙! – sgeddes

0

您需要動態創建必要的SQ。

您所需查詢的SQL需要靜態地知道要創建的列數以及要使用的列名。通過即時創建SQL查詢,然後運行它,可以達到預期的效果。

1

您可以在沒有變量且沒有動態SQL的情況下執行此操作。這個想法是使用子查詢和,然後來獲取有關列的信息。

要得到所有的孩子:

SELECT parent.id, group_concat(child.id) as children, count(*) as numchildren 
FROM parent LEFT JOIN 
    child ON parent.id = child.parent_id 
GROUP BY parent.id 

現在提取這些,使用substring_index()reverse()。這是一個複雜的表達式,從返回列表中的第n個值:

select parent_id, 
     substring_index(children, ',', 1) as child1, 
     reverse(substring_index(reverse(substring_index(children, ',', 2)), ',', 1)) as child2, 
     reverse(substring_index(reverse(substring_index(children, ',', 3)), ',', 1)) as child3, 
     reverse(substring_index(reverse(substring_index(children, ',', 4)), ',', 1)) as child4 
from (SELECT parent.id, group_concat(child.id) as children, count(*) as numchildren 
     FROM parent LEFT JOIN 
      child ON parent.id = child.parent_id 
     GROUP BY parent.id 
    ) t 
相關問題