在SQL表中查找堆叠/重叠日期交叉点的总和

时间:2015-03-11 14:28:33

标签: mysql datetime

我有下表代表文章的预订:

+---+------------+----------+-------------+-------------+
|id | article_id | quantity |  starts_at  |   ends_at   |
+---+------------+----------+-------------+-------------+
| 1 |          1 |        1 | 2015-03-01  | 2015-03-20  |
| 2 |          1 |        2 | 2015-03-02  | 2015-03-03  |
| 3 |          1 |        3 | 2015-03-04  | 2015-03-15  |
| 4 |          1 |        2 | 2015-03-16  | 2015-03-22  |
| 5 |          1 |        2 | 2015-03-11  | 2015-03-19  |
| 6 |          2 |        2 | 2015-03-06  | 2015-03-22  |
| 7 |          2 |        3 | 2015-03-02  | 2015-03-04  |
+---+------------+----------+-------------+-------------+

从这张表中我想提取以下信息:

+------------+----------+
| article_id | sum      |  
+------------+----------+
|          1 |        6 | 
|          2 |        3 | 
+------------+----------+

Sum表示给定时间范围内堆叠/重叠预订商品数量的最大总和。在第一个表中,id为1的文章的最大值是预订1,3和5。

是否有任何MySQL解决方案可以从这样的表中获取此信息?

非常感谢!

编辑:日期交叉点至关重要。让我们说预订5从2015-03-17开始,article_id = 1的结果为5,因为预订3和5不再重叠。 sql应该自动考虑所有可能的重叠可能性。

3 个答案:

答案 0 :(得分:0)

select sum(quantity) from ... 
group by article_id 
where  
... select your date range ...

答案 1 :(得分:0)

这应该有效。 两组。首先得到可能的数量的明确列表&#39 ;;第二 - 总结他们

SELECT article_id, SUM(sub.quantity) FROM
(SELECT article_id, quantity FROM table GROUP BY article_id, quantity) as sub
GROUP BY article_id

答案 2 :(得分:0)

我的回答似乎很复杂,也许;但事实并非如此,如果人们接受使用日历表是处理日期范围相关问题的优秀MySQL习语。我仔细调整了Artful Software's calendar table文章中的日历表代码。 Artful Software's query techniques是在MySQL中处理复杂事物的绝佳资源。日历表为您提供了每个日期的行,这使得许多事情变得更加容易。

对于下面的全部内容,you can go to this sqlfiddle是一个可以玩代码的地方。加载需要一段时间。

首先,这是您的数据:

CREATE TABLE articles
(`id` int, `article_id` int, `quantity` int, `starts_at` datetime, `ends_at` datetime);


INSERT INTO articles
    (`id`, `article_id`, `quantity`, `starts_at`, `ends_at`)
VALUES
    (1, 1, 1, '2015-03-01 00:00:00', '2015-03-20 00:00:00'),
    (2, 1, 2, '2015-03-02 00:00:00', '2015-03-03 00:00:00'),
    (3, 1, 3, '2015-03-04 00:00:00', '2015-03-15 00:00:00'),
    (4, 1, 2, '2015-03-16 00:00:00', '2015-03-22 00:00:00'),
    (5, 1, 2, '2015-03-11 00:00:00', '2015-03-19 00:00:00'),
    (6, 2, 2, '2015-03-06 00:00:00', '2015-03-22 00:00:00'),
    (7, 2, 3, '2015-03-02 00:00:00', '2015-03-04 00:00:00');

接下来,这里是日历表的创建 - 我创建了比需要更多的日期行(回到年初,然后到明年开始)。理想情况下,您只需永久保留一个更大的日历表,涵盖一系列日期,可以处理您可能需要的任何事情。 以下所有内容看起来都很冗长复杂。但是如果你已经有一个日历表,那么下一部分就没有必要了。

CREATE TABLE calendar ( dt datetime primary key ); 

/* the views below will be joined and rejoined to themselves to
   get the effect creating many rows.  V ends up with 10 rows. */
CREATE OR REPLACE VIEW v3 as SELECT 1 n UNION ALL SELECT 1 UNION ALL SELECT 1; 
CREATE OR REPLACE VIEW v as SELECT 1 n FROM v3 a, v3 b UNION ALL SELECT 1;

/* Going to limit the calendar table to first of year of min date
   and first of year after max date */
SELECT @min := makedate(year(min(starts_at)),1) FROM articles;
SELECT @max := makedate(year(min(ends_at))+1,1)  FROM articles;
SET @inc = -1;

/* below we work with @min date + @inc days successively, with @inc:=@inc+1
   acting like ++variable, so we start with minus 1.
   We insert as many individual date rows as we want by self-joining v,
   and using some kind of limit via WHERE to keep the calendar table small
   for our example. For n occurrences of v below, you get a max
   of 10^n rows in the calendar table. We are using v as row-creation
   engine. */
INSERT INTO calendar 
SELECT @min + interval @inc:=@inc+1 day as dt 
FROM v a, v b, v c, v d # , v e , v f
WHERE @inc < datediff(@max,@min);

现在我们已经准备好找到堆叠了。假设上述(我知道很大的假设),这变得相当容易。为了便于阅读,我将通过几个视图来实现。

/* now create a view that will let us easily view the articles
   related to indvidual dates when we query.
   Not necessary, just makes things easier to read. */
CREATE OR REPLACE VIEW articles_to_dates as
SELECT c.dt, article_id
FROM articles a
INNER JOIN calendar c on c.dt between (SELECT min(starts_at) FROM articles) and (SELECT max(ends_at) FROM articles)
GROUP BY article_id, c.dt;

--SELECT * FROM articles_to_dates  --This query would show the view's result

/* next view is the total amount of articles booked per individual date */
CREATE OR REPLACE VIEW booked_quantities_per_day AS
SELECT a2d.dt,a2d.article_id, SUM(a.quantity) as booked_quantity
FROM  articles_to_dates a2d
INNER JOIN articles a on a2d.dt between a.starts_at and a.ends_at and a.article_id = a2d.article_id
GROUP BY a2d.dt, a2d.article_id 
ORDER by a2d.article_id, a2d.dt

--SELECT * from booked_quantities_per_day --this query would show the view's result

最后,这是理想的结果:

SELECT article_id, max(booked_quantity) max_stacked 
FROM booked_quantities_per_day
GROUP BY article_id;

结果:

article_id  max_stacked
1   6
2   3