【问题标题】:MySQL INNER JOIN with GROUP BY and COUNT(*)MySQL INNER JOIN 与 GROUP BY 和 COUNT(*)
【发布时间】:2020-03-05 06:48:18
【问题描述】:

我一直无法理解 INNER JOIN(或任何其他 JOIN 类型),因此我正在努力弄清楚如何在我的特定情况下使用它。事实上,我什至不确定它是否是我需要的。我查看了其他示例并阅读了教程,但我的大脑似乎并没有按照真正获得它所需的方式工作(或者它根本不起作用)。

这是场景:

我有两张桌子 -

  1. phone_numbers - 此表包含电话号码列表 属于许多不同的客户。单个客户可以拥有 多个号码。为简单起见,我们会说这些字段是 'number_id'、'customer_id'、'phone_number'。
  2. call_history - 此表记录了其中一个调用的每个调用 第一个表中的数字本来可以的。有记录为 每个人的电话都可以追溯到几年前。再次,为简单起见, 我们会说相关字段是 customer_id、phone_number、 call_start_time。

我想要完成的是在电话号码表中找到属于特定 customer_id 的所有号码,并使用该信息搜索 call_history 表并找到每个电话号码已收到的电话数量,并按每个号码的呼叫次数对其进行分组,最好在一个号码根本没有接到任何呼叫的情况下也显示零。

零调用很重要的原因是因为这是我感兴趣的数据。否则,我可以从 call_history 表中获取所有信息。但我想要实现的是找到没有活动的数字。

我能够完成的只是运行一个查询来获取属于一位客户的所有号码:

SELECT customer_id, phone_number FROM phone_numbers WHERE customer_id = Y;

然后运行第二个查询以获取该 customer_id 在设定持续时间内的所有电话:

SELECT customer_id, phone_number, COUNT(*) FROM call_history WHERE customer_id = Y and call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) GROUP BY phone_number;

然后我不得不使用从两个查询返回的数据,并在 Excel 中使用 VLOOKUP 函数将第二个查询中每个单独号码的呼叫次数与第一个查询中的所有号码列表相匹配,从而留下空白在我的“所有号码”表中,并找出那些在那个时间段内没有来电的号码。

我希望有某种方法可以通过单个查询完成所有这些并返回一个结果表,列出零次调用并消除整个手动 Excel 位,因为它不会过于高效且容易受到人为影响错误。

【问题讨论】:

  • call_start_time 字段的类型是什么?
  • 请展示你能做的部分。请给minimal reproducible example。询问 1 个明确的特定非重复问题,重新定位您遇到的第一个问题。请参阅 How to Ask、其他 help center 链接和投票箭头鼠标悬停文本。是时候学习一本关于 SQL 的介绍性教科书了。 PS x inner join y on c 返回粘贴到满足 c 的 y 行的 x 行的每一行。即 x 交叉连接 y 其中 c。那么,您想要 x 交叉连接 y 行中的哪一个?等等。问你第一次卡在哪里。
  • 我敢打赌,一旦您了解了所有 JOIN 的功能,就很容易实现这一点。

标签: mysql group-by count inner-join


【解决方案1】:

如果没有您提供的至少一个可行的示例,要重新创建您的情况并不容易。无论如何,INNER JOIN 可能不会按照您的预期返回结果。在我使用 MySQL 的短暂时间里,我主要使用两种类型的JOIN;一个已经提到过,另一个是LEFT JOIN。根据我对您的问题的理解,您想要实现的目标可以通过使用LEFT JOIN 而不是INNER JOIN 来完成。我可能不是向您解释这一点的最佳人选,但我是这样理解的:

INNER JOIN - 仅返回两个(或多个)表之间的 ON 子句中匹配的任何内容。

LEFT JOIN - 将返回连接左侧表中的所有内容,如果 ON 在连接右侧的表中没有匹配项,则返回 NULL .. 除非您从右侧的某些内容中指定了某些 WHERE 条件表。

现在,这是我的查询建议,希望对您有用:


SELECT A.customer_id, A.phone_number, 
       SUM(CASE WHEN call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) 
                THEN 1 ELSE 0 END) AS Total 
FROM phone_numbers A 
LEFT JOIN call_history B
ON A.customer_id=B.customer_id 
GROUP BY A.customer_id,A.phone_number;

我在这里所做的是我 LEFT JOIN phone_numbers 表在 customer_id 上带有 call_history 并且我将 WHERE call_start_time >= .. 条件重新定位到 CASE 表达式中的 SELECT 因为将它放在 WHERE 将把它变成改为普通连接或内部连接。

这是一个小提琴示例:https://www.db-fiddle.com/f/hriFWqVy5RGbnsdj8i3aVG/1

【讨论】:

  • 感谢您提供此信息。它非常接近,但是我在您的小提琴中为客户“Y”添加了一个额外的号码,没有该号码的 call_history 记录,我希望结果中会在“总计”列中显示“0”。相反,它显示了一个“3”,这是另一个号码在该时间范围内的呼叫次数。如果您想了解我的意思,我已经更新了小提琴。与此同时,我会继续玩。
  • 我计算出(通过大部分猜测)我需要改变什么以获得我想要的。我需要 LEFT JOIN call_history B ON A.phone_number=B.phone_number 而不是 LEFT JOIN call_history B ON A.customer_id=B.customer_id。我现在得到零,我应该得到零。还添加了一个 WHERE 以仅过滤掉我想要的 customer_id。谢谢。
  • 我没有预见到有类似 customer_id 不同号码的可能性,但我很高兴你设法解决了@MarkJohnson。你可以用你的最终查询和更新的小提琴来回答你的问题:)
【解决方案2】:

对于内连接你应该这样做..

SELECT customer_id,phone_number FROM phone_numbers as pn,call_history as ch where pn.customer_id = ch.customer_id and call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) GROUP BY phone_number;

只要添加你想加入的表名并添加条件

【讨论】:

  • 请在发帖前查看编辑框下方帖子的格式化版本。阅读代码和引用的内联和块格式的编辑帮助。
  • 以上是有效解决方案的sql没有问题。
猜你喜欢
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
  • 2020-05-17
  • 1970-01-01
  • 1970-01-01
  • 2019-03-21
  • 1970-01-01
  • 2023-03-12
相关资源
最近更新 更多