1

我有时会遇到这样一种情况,即我有父/子关系,我想为每个父级选择所有子级。

这是一个肤浅的例子。

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
4

3 回答 3

2

您要查找的内容称为PIVOT. MySQL 不支持该PIVOT命令,但您可以使用MAXwith来模拟它CASE

如果您知道孩子的数量或者您可以拥有最大数量,这将很有用。

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;
于 2013-05-31T01:24:54.507 回答
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()and 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
于 2013-05-31T01:24:00.317 回答
0

您需要动态创建必要的 SQ。

您所需查询的 SQL 需要静态知道要创建的列数以及要使用的列名。通过动态创建 SQL 查询,然后运行它,您可以达到预期的效果。

于 2013-05-31T01:18:42.007 回答