【问题标题】:PostgreSQL: COUNT() rows using 2-level joins with ONLY_FULL_GROUP_BY activePostgreSQL:使用 2 级连接的 COUNT() 行且 ONLY_FULL_GROUP_BY 处于活动状态
【发布时间】:2019-10-12 19:21:42
【问题描述】:

问题

我在编写函数式查询计数结果行时遇到了麻烦。

数据库表

我有以下(简化的)表(正在开发的环境是 Drupal 8 btw,使用数据库服务):

nodes 表:

id - 数字 id
title - varchar

示例条目:

id   title
1    My first article
2    My second article
3    My third article

comments 表:

cid - 数字 id
entity_type - varchar,评论实体
entity_id - 数字 id,包含引用
status - 整数,0 表示未发布,1 表示已发布
comment - 文字

示例条目:

cid   entity_type   entity_id   status   comment
1     node          1           1        foo
2     node          1           1        bar
3     comment       1           1        baz
4     node          1           0        spam/foul language/whatever
5     node          2           1        yeeeha

数据结构说明

“节点”可以被注释。 cmets 然后被存储在“cmets”表中。对于每条评论,都有一个专门的行,其中包含评论的 id、评论的实体类型(可以是“节点”和“评论”)以及评论实体的 id。并且 cmets 也可以被评论 - 这些“回复”也被存储在“cmets”表中,因此这些条目包含“评论”作为 entity_id 和回复的评论的 id。

我现在想通过单个查询获得以下结果:

id    title                comments
1     My first article     3
2     My second article    1
3     My third article     0

comments 应包含所有已发布的 cmets 和对给定节点的已发布回复的总和。因此,如果一个节点被直接评论了两次,并且其中一个 cmets 也被评论了,那么 comments 计数应该声明 3。 (注:atm“回复”到cmets无法回复,所以这里只有3级环境(nodecommentcomment)。

正在使用的数据库:

使用的数据库是 PostgreSQL 9.6,ONLY_FULL_GROUP_BY 处于活动状态。

我尝试了什么

我现在已经花了几个小时尝试使用几乎类似于以下内容的查询来查询数据(使用 Drupal 的数据库服务select 接口):

SELECT n.id, n.title, COUNT(c.cid)+COUNTr.cid) AS comments
FROM nodes n
LEFT JOIN comments c
ON c.type = "node" AND n.id = c.entity_id AND c.status = 1
LEFT JOIN comments r
ON r.type = "comment" AND c.id = r.entity_id AND r.status = 1
GROUP BY n.id, n.title, c.entity_id, r.entity_id

但在我的一生中,我只是想不出编写查询的正确方法。我的基本想法是选择基表节点,在此上左连接 cmets 的第一阶段,然后再次左连接对第一个连接的回复。但似乎我的数据库对我的查询有其他想法...¯\_(ツ)_/¯

我真的希望有人可以让我重回正轨。任何帮助是极大的赞赏!感谢您花时间阅读所有这些内容。

【问题讨论】:

    标签: postgresql drupal group-by count drupal-8


    【解决方案1】:

    首先让我们了解您所写和遗漏的内容 - 在查询中您遗漏了 ( 在第一行计数之后。

    其次,您使用额外的“r.entity_id”进行分组,这不是必需的,它会隔离结果。

    按节点id升序排列结果的三阶。

    如果对您有帮助,请使用以下查询并标记答案正确。

    SELECT n.id, n.title, COUNT(c.cid)+COUNT(r.cid) AS comments
    FROM nodes n
     left JOIN comments c
    ON c.entity_type = 'node' AND n.id = c.entity_id AND c.status = 1
     left JOIN comments r
    ON r.entity_type = 'comment' AND c.cid = r.entity_id AND r.status = 1
    GROUP BY n.id, n.title, c.entity_id 
    order by n.id asc
    

    【讨论】:

    • 嗨@Bipin Kumar!感谢您为帮助我所做的努力! :-) 我理解你的评论,它们对我很有价值,帮助我更好地理解加入! GMB 只是比他的回复快了几分钟,他的回答也帮助我解决了我的问题。但我非常感谢您花时间阅读我的问题并提供有用的帮助!
    【解决方案2】:

    你的逻辑很好。您只需要在第一级计算不同的值,并正确处理NULL 值。此外,您希望对来自nodes 的列进行分组,而不是来自comments

    select 
        n.id,
        n.title,
        coalesce(count(distinct c1.cid), 0) + coalesce(count(c2.cid), 0) "comments"
    from nodes n
    left join comments c1 
        on  c1.entity_id = n.id  
        and c1.entity_type = 'node'
        and c1.status = 1
    left join comments c2 
        on c2.entity_id = c1.cid  
        and c2.entity_type = 'comment'
        and c2.status = 1
    group by n.id, n.title
    

    demo on DB Fiddle 与您的示例数据返回:

    | id  | title             | comments |
    | --- | ----------------- | -------- |
    | 1   | My first article  | 3        |
    | 2   | My second article | 1        |
    | 3   | My third article  | 0        |
    

    【讨论】:

    • 嗨@GMB!首先,让我感谢您的帮助!我已经尝试过您的建议,但在我的实际实现中似乎有些偏离。在this pastebin 中查看 Drupal 实际生成的查询。问题在于实际上同一个节点在结果集中多次出现
    • 另外,我别无选择,只能 groupBy cmets - 正如最初所说,ONLY_FULL_GROUP_BY 有效。
    • @Chris:我无法访问 pastebin 链接。但是,我对查询进行了一些修复(缺少条件status = 1)并在小提琴中对其进行了测试。它现在确实返回了预期的结果。请参阅我的更新答案。
    • @Chris:ONLY_FULL_GROUP_BY 强制执行的规则是:每个非聚合列都必须出现在 GROUP BY 子句中。无需在查询中包含 comments 表的列,因为它们出现在聚合表达式中。
    • 哦,伙计! @GMB,我很抱歉。我对您的回答的回复是基于我在尝试您的建议时犯的一个简单的错字。我没有对 entity_id 进行分组,而是将 cid 分组 - 难怪同一个节点被多次列出!谢谢你,你的帮助让我重新上路!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-24
    • 2012-01-03
    • 2021-09-13
    • 2013-08-27
    • 2019-09-01
    • 1970-01-01
    相关资源
    最近更新 更多