【问题标题】:Complex Join - involving date ranges and sum复杂连接 - 涉及日期范围和总和
【发布时间】:2011-02-15 02:24:45
【问题描述】:

我有两个需要加入的表...我想在 'id' 上加入 table1 和 table2 - 但是在表 2 中,id 不是唯一的。我只希望为表二返回一个值,该值表示名为“total_sold”的列的总和 - 在指定的日期范围内(比如一个月),但是我想要同时多个日期范围......

SELECT ta.id, sum(tb.total_sold) as total_sold_this_week, sum(tc.total_sold) as total_sold_this_month
FROM table_a as ta
LEFT JOIN table_b as tb ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 WEEK) AND NOW()
LEFT JOIN table_b as tc ON ta.id=tc.id AND tc.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 MONTH) AND NOW()
GROUP BY ta.id

这有效,但不能对行求和 - 每个 id 只返回一行...如何从表 b 中获取总和而不是仅一行? 请批评问题的格式是否需要更多工作 - 如果需要,我可以重写并提供示例数据 - 这是一个更大问题的简单版本。

-谢谢

【问题讨论】:

  • 我现在得到的是滚动总数。假设我希望我从表 b 中返回的两个值是 10 和 35,我得到的是 45,45...

标签: mysql join sum between


【解决方案1】:

使用子查询

解决此问题的一种方法是使用subqueriesLEFT JOIN 为右表中的每个匹配项创建一个新的“结果”,因此使用两个 LEFT JOIN 会创建比您想要的更多的 ROWS。你可以只选择你想要的值,但这可能很慢:

SELECT ta.id, 
   (SELECT SUM(total_sold) as total_sold 
    FROM table_b 
    WHERE date_sold BETWEEN ADDDATE(NOW(), INTERVAL -1 WEEK) AND NOW()
    AND id=ta.id) as total_sold_this_week, 
   (SELECT SUM(total_sold) as total_sold 
    FROM table_b 
    WHERE date_sold BETWEEN ADDDATE(NOW(), INTERVAL -1 MONTH) AND NOW() 
    AND id = ta.id) as total_sold_this_month 
FROM table_a ta;

结果:

+----+------+------------------ -----+
|编号 | total_sold_this_week | total_sold_this_month |
+----+----------+---------- --+
| 1 | 3 | 7 |
| 2 | 4 | 4 |
| 3 |空 |空 |
+----+----------+---------- --+
3 行一组(0.04 秒)

使用 SUM(CASE ...)

此方法不使用子查询(在较大的数据集上可能会更快)。我们想将 table_a 和 table_b 连接在一起一次,使用我们的“最大”日期范围,然后使用基于 CASESUM() 来计算“较小的范围”。

SELECT ta.*, 
  SUM(total_sold) as total_sold_last_month, 
  SUM(CASE 
    WHEN date_sold BETWEEN NOW() - INTERVAL 1 WEEK AND NOW() 
    THEN total_sold
    ELSE 0 
    END) as total_sold_last_week 
FROM table_a AS ta 
LEFT JOIN table_b AS tb 
   ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 MONTH) AND NOW() 
GROUP BY ta.id;

这将返回与子查询示例几乎相同的结果集:

+----+------------------------+----------------- -----+
|编号 | total_sold_last_month | total_sold_last_week |
+----+------------------------+-------- --+
| 1 | 7 | 3 |
| 2 | 4 | 4 |
| 3 |空 | 0 |
+----+------------------------+-------- --+
3 行一组(0.00 秒)

唯一的区别是0 而不是NULL。您可以使用此方法汇总任意数量的日期范围,但最好将返回的行限制在 ON 子句中的最大范围内。

只是为了展示它是如何工作的:删除 GROUP BYSUM() 调用,并将 date_sold 添加到 SELECT 会返回:

+----+------------+-----------+---- ------------------+
|编号 |售出日期 | total_sold_last_month | total_sold_last_week |
+----+------------+-----------+-------- ---------------+
| 1 | 2010-04-30 | 2 | 2 |
| 1 | 2010-04-24 | 2 | 0 |
| 1 | 2010-04-24 | 2 | 0 |
| 1 | 2010-05-03 | 1 | 1 |
| 2 | 2010-05-03 | 4 | 4 |
| 3 |空 |空 | 0 |
+----+------------+-----------+-------- ---------------+
一组 6 行(0.00 秒)

现在,当您 GROUP BY idSUM() 这两个 total_sold 列时,您就有了结果!

老建议

在混合使用两个不同的日期范围之前,您可以使用GROUP BY 使用 table1 上的表 ID 进行分组,并使用 SUM() 聚合函数将返回的行相加。

SELECT ta.id, SUM(tb.total_sold) as total_sold_this_week
FROM table_a as ta
LEFT JOIN table_b as tb 
ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -3 WEEK) AND NOW()
GROUP BY ta.id
+----+----------+
|编号 | total_sold_this_week |
+----+----------+
| 1 | 7 |
| 2 | 4 |
| 3 |空 |
+----+----------+
3 行一组(0.00 秒)

测试数据

NOW() 是 2010-05-03

mysql> select * from table_a;从 table_b 中选择 *;
+----+
|编号 |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 行一组(0.00 秒)

+----+------------+------------+
|编号 |售出日期 |已售出 |
+----+------------+------------+
| 1 | 2010-04-24 | 2 |
| 1 | 2010-04-24 | 2 |
| 1 | 2010-04-30 | 2 |
| 1 | 2010-05-03 | 1 |
| 2 | 2010-05-03 | 4 |
+----+------------+------------+
5 行一组(0.00 秒)

【讨论】:

  • 好的 - 感谢您的帮助,但我已经移动了球门柱(更接近我的实际问题)- 我实际上需要多个连接到同一张桌子上。
  • @calumbrodie - 不管你做了多少加入,只要你仍然可以按ta.id 分组并且只对一列求和,答案很可能是一样的。你能把完整的查询放在你的问题中吗?
  • @calumbrodie,我相信我已经更新了一个应该可行的答案。
  • 非常感谢您的帮助。 SUM(CASE...) 效果很好。我实际上从第二张表中提取了几个范围(6 个范围),这种方式效果很好。再次感谢!...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-20
  • 1970-01-01
  • 2021-01-14
相关资源
最近更新 更多