【问题标题】:Joins and count accross several tables跨多个表连接和计数
【发布时间】:2012-11-08 22:49:01
【问题描述】:

我正在尝试编写一个复杂的(至少,就我的知识水平而言)字符串,但我有一个地狱般的时间。

这就是问题所在。我有两张表,一张名为 t1,一张名为 c1。

表格定义如下:

table T1:

e_id, char(8),  
e_date, datetime,  
e_status, varchar(2)

table C1:

e_id, char(8),  
e_date, datetime,  
e_status, varchar(2)

每个表都包含一个标识符列表,这些标识符在两个表中可能存在也可能不存在(它们在每个表中可能是唯一的,也可能不是唯一的),以及相关的状态(在 T1 表中可以是“OK”或“R” , 可以是 C1 表中的 'OK' 或 'C'),以及与 e_id 的每次出现相关联的日期时间 e_date

我正在尝试编写一个查询:

  • 检索 T1 表中在过去 24 小时内具有e_date 的所有 e_id 值。
  • 检索过去 30 天内(e_date > 现在 - 30 天)在 T1 中过去 24 小时内(e_date 大于当前时间 - 24 小时)发生的所有 e_id,仍在表 T1 内(例如:如果在 t1 中找到 e_id 的 AAAAAAAAA 和 BBBBBBBB 且 e_date 在过去 24 小时内,则检索 e_id 的 AAAAAAA 和 BBBBBBBB 在同一个表中的所有出现但其 e_date 为过去 30 天内)
  • 将在整个 T1 table 中找到的每个特定 e_ide_status = 'OK' 计数附加到行结果中
  • 将在整个 C1 table 中找到的每个特定 e_ide_Status = 'OK' 计数附加到行结果中

我会尽力在这里写一些示例数据/结果。为清楚起见,我将忽略表数据类型。假设当前日期和时间是 2012-Nov-08 19:00:00

T1:

  1. e_id:'A',e_date:2012-Nov-08 10:00:00,e_status:'OK'
  2. e_id:'A',e_date:2012-Nov-08 10:00:00,e_status:'R'
  3. e_id:'A',e_date:2012-Oct-15 10:00:00,e_status:'R'
  4. e_id: 'B', e_date: 2012-Oct-15 10:00:00, e_status: 'OK'
  5. e_id:'A',e_date:2012-Oct-15 10:00:00,e_status:'OK'
  6. e_id:'A',e_date:2012-Oct-15 10:00:00,e_status:'R'
  7. e_id:'A',e_date:2012-Oct-15 10:00:00,e_status:'R'
  8. e_id:'A',e_date:2010-Jan-01 10:00:00,e_status:'R'
  9. e_id:'A',e_date:2010-Jan-01 10:00:00,e_status:'R'

C1:

  1. e_id:'A',e_date:2012-Oct-01 10:00:00,e_status:'C
  2. e_id: 'B', e_date: 2012-Oct-01 10:00:00, e_status: 'OK'
  3. e_id:'A',e_date:2012-Oct-01 10:00:00,e_status:'C
  4. e_id: 'B', e_date: 2012-Oct-01 10:00:00, e_status: 'OK'
  5. e_id:'A',e_date:2012-Oct-01 10:00:00,e_status:'OK' 李>

运行查询将产生:

e_id、e_date、e_status、r_count、c_count
1. e_id: 'A', e_date: 2012-Nov-08 10:00:00, e_status: 'OK', r_count: 6,c_count:2
2. e_id: 'A', e_date: 2012-Nov-08 10:00:00, e_status: 'R', r_count: 6,c_count:2
3. e_id: 'A', e_date: 2012-Oct-15 10:00:00, e_status: 'R', r_count: 6,c_count:2
4. e_id: 'A', e_date: 2012-Oct-15 10:00:00, e_status: 'OK', r_count: 6,c_count:2
5. e_id: 'A', e_date: 2012-Oct-15 10:00:00, e_status: 'R', r_count: 6,c_count:2
6. e_id: 'A', e_date: 2012-Oct-15 10:00:00, e_status: 'R', r_count: 6,c_count:2

非常抱歉,我不得不将 T1 第 3 行到第 7 行(结果的第 3 4 5 6 行)的日期更改为错误值。

未返回 T1 的第 4 行,因为在过去 24 小时内未找到 e_id: B
T1 第 8 行和第 9 行未返回,因为它们在过去 30 天之外

【问题讨论】:

  • 你能提供表格的定义吗?另外,我不知道您所说的第二个要点是什么意思。您想要过去 30 天内的计数以及整个表的计数吗?如果不是,子弹没有意义。
  • 好了,我已经修改了帖子。我希望这可以澄清,我感谢您的回复!
  • 包含一些示例数据和所需输出的(相关)表列将有很大帮助

标签: mysql sql count sum self-join


【解决方案1】:

是时候做一些 TDQD — 测试驱动的查询设计了。

T1 中过去 24 小时内的行数

SELECT DISTINCT e_id
  FROM T1
 WHERE e_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)

这将是查询其他部分中普遍存在的子查询。

T1 中过去 30 天的行数...

...过去 24 小时内 T1 中有条目。

SELECT a.e_id
  FROM t1 AS a
  JOIN (SELECT DISTINCT e_id
          FROM T1
         WHERE e_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
       ) AS b ON b.e_id = a.e_id
 WHERE a.e_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)

我们可以根据需要添加其他列。

T1 中状态为“R”的行数 ...

...过去 24 小时内 T1 中的条目

SELECT a.e_id, COUNT(*) AS r_count  -- Per question; why not t_count?
  FROM t1 AS a
  JOIN (SELECT DISTINCT e_id
          FROM T1
         WHERE e_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
       ) AS b ON b.e_id = a.e_id
 WHERE a.e_status = 'R'
 GROUP BY a.e_id

C1 中状态为“C”的行数 ...

...过去 24 小时内 T1 中的条目

SELECT a.e_id, COUNT(*) AS c_count
  FROM c1 AS a
  JOIN (SELECT DISTINCT e_id
          FROM T1
         WHERE e_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
       ) AS b ON b.e_id = a.e_id
 WHERE a.e_status = 'C'
 GROUP BY a.e_id

组装查询集以产生结果

SELECT a.e_id, a.e_date, a.e_status, c.r_count, d.c_count
  FROM t1 AS a
  JOIN (SELECT DISTINCT e_id
          FROM T1
         WHERE e_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
       ) AS b ON b.e_id = a.e_id
  LEFT JOIN -- Because there might be no OK rows in T1
       (SELECT a.e_id, COUNT(*) AS r_count
          FROM t1 AS a
          JOIN (SELECT DISTINCT e_id
                  FROM T1
                 WHERE e_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
               ) AS b ON b.e_id = a.e_id
         WHERE a.e_status = 'OK'
         GROUP BY a.e_id
       ) AS c ON c.e_id = a.e_id
  LEFT JOIN -- Because there might be no OK rows in C1
       (SELECT a.e_id, COUNT(*) AS c_count
          FROM c1 AS a
          JOIN (SELECT DISTINCT e_id
                  FROM T1
                 WHERE e_date >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
               ) AS b ON b.e_id = a.e_id
         WHERE a.e_status = 'OK'
         GROUP BY a.e_id
       ) AS d ON d.e_id = a.e_id
 WHERE a.e_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)

您可能可以编写没有 24 小时子子查询的子查询,但尽快消除尽可能多的行可能会有效。


TDQD 背后的概念的一个优点是您可以检查中期结果。有一些基本上是微不足道的语法问题(部分原因是 MySQL 不是我的主要 DBMS),但是两个 COUNT 子查询从 JOIN 到 LEFT JOIN 的变化是您在组装查询时容易发现的事情.第一次尝试把所有事情都做好是很困难的,如果不是徒劳的话。但是一步一步的积累可以让你对你所做的事情充满信心。如果不测试组件子查询,我永远不会从头开始构建如此复杂的查询。

感谢(次要)更新,FatalMojo

【讨论】:

  • 我的想法,你刚刚吹了先生!我会尽快尝试(我现在无法访问服务器)。感谢您提供的惊人答案和详细信息。我会让你知道结果如何!
  • 如果最终结果不正确,则检查各个查询,调整每个查询直到正确,然后组合结果。您甚至可能决定在尝试最终结果之前检查第一个查询——即在其他任何地方成为子查询的那个。请注意,我不使用 MySQL;我不确定 DATE_SUB() 语法,但我确实检查了documentation。与其他 SQL DBMS 相比,就功能实现方式而言,日期和时间是 SQL DBMS 中变化最大的部分。
  • 哈哈,到目前为止一切顺利,我正在一次处理一个查询并消化它!我实际上设置了一个带有示例表的本地服务器,因为我等不及了! DATE_SUB 很棒,但是,在 MySQL 中间隔不是复数,所以它是 DAY 而不是 DAYS,HOUR 而不是 HOURS,等等。我认为这会奏效,我非常感激! :D
  • 不用道歉!我已经通过你的帖子意识到了很多事情!最后一件事,如果 r_count 或 c_count 为 0,这似乎省略了行,有什么方法可以显示这些行吗?
  • 好的,两个计数查询上的 LEFT JOIN 成功了 :) 乔纳森,谢谢你的回答。我希望有一天我能够将知识传递下去!
猜你喜欢
  • 1970-01-01
  • 2021-07-15
  • 2014-12-29
  • 1970-01-01
  • 2010-10-21
  • 2016-02-17
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多