【问题标题】:Counting number of joined rows in left join计算左连接中的连接行数
【发布时间】:2013-12-05 20:38:23
【问题描述】:

我正在尝试在 SQL 中编写一个聚合查询,它返回连接到表中给定记录的所有记录的计数;如果没有记录连接到给定记录,那么该记录的结果应该是0

数据

我的数据库看起来像这样(很遗憾,我无法更改结构):

MESSAGE
----------------------------------------------
MESSAGEID   SENDER        SUBJECT
----------------------------------------------
1           Tim           Rabbit of Caerbannog
2           Bridgekeeper  Bridge of Death

MESSAGEPART
----------------------------------------------
MESSAGEID   PARTNO        CONTENT
----------------------------------------------
1           0             (BLOB)
1           1             (BLOB)
3           0             (BLOB)

MESSAGEPART 有一个复合 PRIMARY KEY("MESSAGEID", "PARTNO")

期望的输出

根据上面的数据,我应该得到这样的结果:

MESSAGEID   COUNT(*)
-----------------------------------------------
1           2
2           0

很明显,我需要对MESSAGE 表进行左连接,但是对于来自MESSAGEPART 的连接列是NULL 的行,我如何返回0 的计数?我尝试了以下方法:

逻辑

我试过了

SELECT m.MESSAGEID, COUNT(*) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY m.MESSAGEID;

但是,这会返回

MESSAGEID   COUNT(*)
-----------------------------------------------
1           2
2           1

我也试过

SELECT mp.MESSAGEID, COUNT(*) FROM MESSAGE m
LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
GROUP BY mp.MESSAGEID;

但这会返回

MESSAGEID   COUNT(*)
-----------------------------------------------
1           2
            1

我在这里做错了什么?

【问题讨论】:

  • 首先,使用m.MESSAGEID(永远存在)而不是mp.MESSAGEID(只有在匹配时才会存在)。

标签: sql oracle join count left-join


【解决方案1】:

根据匹配列返回一个数字作为两个表之间匹配元素的总数

在我的例子中,我需要为来自特定列和两个不同表的匹配项的数量/计数返回一个总数。

例如,我有两个单独的表,每个表都有一个 PhoneNumber 列。在这两个表之间,我想知道该列中有多少匹配。

参考:https://www.guru99.com/joins.html

使用上面相同的表名,它看起来像这样:

SELECT COUNT(DISTINCT m.MESSAGEID) AS COUNT FROM MESSAGE m, MESSAGEPART mp
where mp.MESSAGEID = m.MESSAGEID;

【讨论】:

    【解决方案2】:

    不要忘记使用 DISTINCT,以防您将 LEFT JOIN 多个表:

    SELECT m.MESSAGEID, COUNT(DISTINCT mp.MESSAGEID) FROM MESSAGE m
    LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
    GROUP BY m.MESSAGEID;
    

    【讨论】:

      【解决方案3】:

      这样的事情怎么样:

      SELECT m.MESSAGEID, sum((case when mp.messageid is not null then 1 else 0 end)) FROM MESSAGE m
      LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
      GROUP BY m.MESSAGEID;
      

      COUNT() 函数将对每一行进行计数,即使它为空。使用 SUM() 和 CASE,您可以只计算非空值。

      编辑:取自顶部评论的更简单版本:

      SELECT m.MESSAGEID, COUNT(mp.MESSAGEID) FROM MESSAGE m
      LEFT JOIN MESSAGEPART mp ON mp.MESSAGEID = m.MESSAGEID
      GROUP BY m.MESSAGEID;
      

      希望对您有所帮助。

      【讨论】:

      • 可以简化一点。 sum(case....end) 可以替换为简单的count(mp.messageid)Count(*) 计算在内,包括空值在内的所有内容,count(col_name) 仅计算非空值。
      • 谢谢@NicholasKrasnov。我完全忘记了这种区别。我越老,我就越忘记....叹息.... :-)
      • 这很有用。我需要查看一个链接键旁边的其他一些字段。我发现您可以通过将字段添加到选择列表和 group by 子句来做到这一点。
      • 在尝试了几十个其他类似问题的答案之后,这终于对我有用了。正是我之前没有包含的 GROUP BY 使这项工作有效,而不必使用子查询(您想避免这种情况)。 注意:如果您想添加 WHERE 子句,则需要如下所示:WHERE (mp.PARTNO = 1 OR mp.PARTNO IS NULL) 并添加在 GROUP BY 之前
      • 但是如果你不想分组怎么办:(
      【解决方案4】:

      首先想在加入之前计入你的messaepart表,我想。试试这个:

         SELECT m.MessageId
              , COALESCE(c, 0) as myCount
           FROM MESSAGE m
      LEFT JOIN (SELECT MESSAGEID
                      , count(*) c 
                   FROM MESSAGEPART 
                  GROUP BY MESSAGEID) mp
             ON mp.MESSAGEID = m.MESSAGEID
      

      【讨论】:

      • 这比上面的解决方案更复杂,但无论如何我必须将此查询嵌入另一个查询,因此您的解决方案最终也很有帮助;干杯
      • 无论这可能是或可能不是效率低下,但出于知识的目的,它非常有用,尤其是现在!
      猜你喜欢
      • 1970-01-01
      • 2013-07-12
      • 1970-01-01
      • 1970-01-01
      • 2011-01-12
      • 1970-01-01
      • 2019-05-19
      • 1970-01-01
      • 2019-07-29
      相关资源
      最近更新 更多