如何在一个查询中的多个子查询上正确使用多个 group_concats 而没有区别?

Posted

技术标签:

【中文标题】如何在一个查询中的多个子查询上正确使用多个 group_concats 而没有区别?【英文标题】:How do you properly use multiple group_concats on multiple subqueries within one query without distinct? 【发布时间】:2021-03-12 22:32:41 【问题描述】:

我有一个相当可怕的查询,我正在为特定用户或多个用户提取信息。我需要为该用户获取多组标准化属性,以便通过每个链接表进行访问。

我想将这些集合作为逗号分隔的列表拉回,这似乎是 group_concat 构建的,但我不断得到大量额外的结果,每个额外的连接都会增加。虽然我想我知道原因,但我很难想象如何构建查询以避免这种情况。

我创建了一个简化版本的我在小提琴here 中尝试做的事情。如果该链接不可用,这里是 DDL:

CREATE TABLE users (ID INT, name VARCHAR(30));

INSERT INTO users (ID, name)
VALUES (1, 'Jon');
INSERT INTO users (ID, name)
VALUES (2, 'Jane');

CREATE TABLE skills (ID INT, name VARCHAR(30), groupName VARCHAR(30));

INSERT INTO skills (ID, name, groupName)
VALUES (1, 'Drawing', 'Art');
INSERT INTO skills (ID, name, groupName)
VALUES (2, '3D Animation', 'Art');
INSERT INTO skills (ID, name, groupName)
VALUES (3, 'javascript', 'Programming');
INSERT INTO skills (ID, name, groupName)
VALUES (4, 'html', 'Programming');

CREATE TABLE users2skills (UID INT, SID INT);

INSERT INTO users2skills (UID, SID)
VALUES (1, 3);
INSERT INTO users2skills (UID, SID)
VALUES (1, 4);
INSERT INTO users2skills (UID, SID)
VALUES (2, 1);
INSERT INTO users2skills (UID, SID)
VALUES (2, 2);
INSERT INTO users2skills (UID, SID)
VALUES (2, 4);

CREATE TABLE shifts (ID INT, name VARCHAR(30));

INSERT INTO shifts (ID, name)
VALUES (1, 'Daylight');
INSERT INTO shifts (ID, name)
VALUES (2, 'Evening');
INSERT INTO shifts (ID, name)
VALUES (3, 'Midnight');

CREATE TABLE users2shifts (UID INT, SID INT);
INSERT INTO users2shifts (UID, SID)
VALUES (1, 1);
INSERT INTO users2shifts (UID, SID)
VALUES (1, 2);
INSERT INTO users2shifts (UID, SID)
VALUES (2, 2);
INSERT INTO users2shifts (UID, SID)
VALUES (2, 3);

首先,这是我汇总的一个简单查询,它得出了预期的结果:

select  u.ID,
        u.name,
        GROUP_CONCAT(skQ.ID order by skQ.ID) as skillList,
        GROUP_CONCAT(skQ.name order by skQ.ID) as skillDesc
        
from    users u
        left outer join (
                            select  u2sk.UID, sk.ID, sk.name
                            from    users2skills u2sk
                                    inner join skills sk    on sk.ID    = u2sk.SID
                            ) skQ on skQ.UID    = u.ID
group by u.ID,
        u.name

结果如下所示:

ID name skillList skillDesc
1  Jon  3,4    javaScript,HTML
2  Jane 1,2,4  Drawing,3D Animation,HTML

请注意,两个列表的顺序匹配,因此我可以稍后以编程方式将 ID 加入到描述中。但是,我需要额外的数据,包括技能的类别描述,并且想要一个看起来像这样的最终结果集:

ID name skillList skillDesc      skillGroups             shiftList shiftDesc
1  Jon  3,4      javaScript,HTML Programming,Programming 1,2     Daylight,Evening
2 Jane 1,2,4    Drawing,3D Animation,HTML Art,Art,Programming 2,3 Evening,Midnight

所以我为我希望加入的新表添加了几个新的 group_concat 语句,创建了这个查询:

select  u.ID,
        u.name,
        GROUP_CONCAT(skQ.ID order by skQ.ID) as skillList,
        GROUP_CONCAT(skQ.name order by skQ.ID) as skillDesc,
        GROUP_CONCAT(skQ.groupName order by skQ.ID) as skillGroups,
        GROUP_CONCAT(shQ.ID order by skQ.ID) as shiftList,
        GROUP_CONCAT(shQ.name order by skQ.ID) as shiftDesc
        
from    users u
        left outer join (
                            select  u2sk.UID, sk.ID, sk.name, sk.groupName
                            from    users2skills u2sk
                                    inner join skills sk    on sk.ID    = u2sk.SID
                            ) skQ on skQ.UID    = u.ID
        left outer join (
                            select  u2sh.UID, sh.ID, sh.name
                            from    users2shifts u2sh
                                    inner join shifts sh    on sh.ID    = u2sh.SID
                            ) shQ on shQ.UID    = u.ID
group by u.ID,
        u.name

但是,那返回的结果集是:

ID  name    skillList   skillDesc                                           skillGroups                                     shiftList       shiftDesc
1   Jon     3,3,4,4     javaScript,javaScript,HTML,HTML                     Programming,Programming,Programming,Programming     2,1,2,1     Evening,Daylight,Evening,Daylight
2   Jane    1,1,2,2,4,4 Drawing,Drawing,3D Animation,3D Animation,HTML,HTML Art,Art,Art,Art,Programming,Programming         3,2,3,2,3,2     Midnight,Evening,Midnight,Evening,Midnight,Evening

在我的实际查询中,这个问题成倍增加,每个列表包含 100 多个项目。我已经看到了很多关于如何解决这个问题的问题,但我发现一般都会得到相同的答案:使用 distinct。这给我带来了一个问题,因为我在“groupName”字段中有重复的值,这些值被不同的过滤掉了。这会导致列表大小不同,并阻止我在取回数据后对其进行任何处理。

如果我能提供帮助,我宁愿不需要进行十个单​​独的查询,以及 Web 和 DB 服务器之间的所有相关开销和连接。我怀疑一个查询无论如何都会快得多。以我需要的格式获取所需数据的正确方法是什么?

【问题讨论】:

FWIW,我认为数据显示的问题最好在应用程序代码中解决 @Strawberry 我自己也开始按照这些思路进行思考。但在这一点上,我真的很想知道我决定去的任何一种方式的解决方案,因为我已经投入了太多时间,至少不能离开一堂课。 好吧,也许这就是教训 【参考方案1】:

两个left joined 表中有多个匹配主表中的给定行,因此行被相乘,并且您最终得到相同的值被多次聚合。

基本上,你需要在子查询中聚合:

select u.ID, u.name, skQ.skillList, skQ.skillDesc, sqQ.skillGroups, shQ.shiftList, shQ.shiftDesc
from users u
left outer join (
    select u2sk.UID, 
        GROUP_CONCAT(sk.ID        order by sk.ID) as skillList
        GROUP_CONCAT(sk.name      order by sk.ID) as skillDesc,
        GROUP_CONCAT(sk.groupName order by sk.ID) as skillGroups
    from users2skills u2sk
    inner join skills sk on sk.ID = u2sk.SID
    group by u2sk.uid
) skQ on skQ.UID  = u.ID
left outer join (
    select u2sh.UID, 
        GROUP_CONCAT(sh.ID   order by sh.ID) as shiftList,
        GROUP_CONCAT(sh.name order by sh.ID) as shiftDesc
    from users2shifts u2sh
    inner join shifts sh on sh.ID = u2sh.SID
) shQ on shQ.UID = u.ID

【讨论】:

谢谢。这很有意义,并且按预期工作。

以上是关于如何在一个查询中的多个子查询上正确使用多个 group_concats 而没有区别?的主要内容,如果未能解决你的问题,请参考以下文章

为来自不同表的两个单独操作运行多个子查询(相关)并加入一个表[关闭]

它是如何工作的? - 查询中的多个子选择

如何在子查询的 WHERE 子句中使用来自 UNNEST 的多个值?

多个连接子查询 SQL Server 2008

如何正确索引在具有多个连接的查询中使用的表

如何使用具有多个 GROUP BY、子查询和 WHERE IN 在大表上的查询来优化查询?