复杂加入 - 涉及日期范围和总和

时间:2010-05-03 15:48:36

标签: mysql join sum between

我有两个表需要加入...我想在'id'上加入table1和table2 - 但是在表2中,id不是唯一的。我只希望为表2返回一个值,并且此值表示在指定日期范围(比如一个月)内名为“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中获取总和而不是只有一行? 请批评问题的格式是否可以使用更多的工作 - 如果需要,我可以重写并提供样本数据 - 这是一个更大问题的平凡版本。

-Thanks

1 个答案:

答案 0 :(得分:6)

使用子查询

解决此问题的一种方法是使用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;

结果:

+----+----------------------+-----------------------+
| id | total_sold_this_week | total_sold_this_month |
+----+----------------------+-----------------------+
|  1 |                    3 |                     7 |
|  2 |                    4 |                     4 |
|  3 |                 NULL |                  NULL |
+----+----------------------+-----------------------+
3 rows in set (0.04 sec)

使用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;

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

+----+-----------------------+----------------------+
| id | total_sold_last_month | total_sold_last_week |
+----+-----------------------+----------------------+
|  1 |                     7 |                    3 |
|  2 |                     4 |                    4 |
|  3 |                  NULL |                    0 |
+----+-----------------------+----------------------+
3 rows in set (0.00 sec)

唯一的区别是0而不是NULL。您可以使用此方法汇总尽可能多的日期范围,但仍可能最好将返回的行限制为ON子句中的最大范围。

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

+----+------------+-----------------------+----------------------+
| id | date_sold  | 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 | NULL       |                  NULL |                    0 |
+----+------------+-----------------------+----------------------+
6 rows in set (0.00 sec)

现在当你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
+----+----------------------+
| id | total_sold_this_week |
+----+----------------------+
|  1 |                    7 |
|  2 |                    4 |
|  3 |                 NULL |
+----+----------------------+
3 rows in set (0.00 sec)

测试数据

NOW()是2010-05-03

mysql> select * from table_a; select * from table_b;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)

+----+------------+------------+
| id | date_sold  | total_sold |
+----+------------+------------+
|  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 rows in set (0.00 sec)