假设我有一张满是订单的桌子。每天可以有多个订单,总金额不同:
# Create orders table
CREATE TABLE orders (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`total` decimal(9,2) DEFAULT NULL,
PRIMARY KEY (`id`)
);
我想为这个表创建一堆随机测试数据,所以我创建了一个存储过程:
# Create test data for orders table
DELIMITER $$
CREATE PROCEDURE prepare_orders_data()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE j INT DEFAULT FLOOR(RAND() * 10 + 1); # Number of order entries in one day
# randomized between 1 and 10 days
# Create 2 years worth of data from 2014-01-01 to 2015-12-31
WHILE i < (365*2) DO
WHILE j > 0 DO
INSERT INTO orders(order_date, total)
VALUES (DATE_ADD('2014-01-01', INTERVAL 1*i DAY), RAND() * 100);
SET j = j - 1;
END WHILE;
SET j = FLOOR(RAND() * 10 + 1); #Random number of days from 1-10
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
CALL prepare_orders_tlano_data();
既然我有数据,我想要做的是按日期获得订单总和的总和,并将它们与上一年的订单总和进行比较。前一年定义为当前日期之前的52周(我想查看由星期几确定的日期,例如:感恩节,黑色星期五,复活节等。大多数情况下,日期将是比较是休息一天(例如:2015-04-08与2015-09-08相比)。
我已经找到了解决方案,但我不相信它是最佳的。这是因为我基本上加入了同一个表,我认为有更快的方法来做到这一点。这是解决方案:
# Get totals for dates and dates from 52 weeks previous
# (same weekday approximately 1 year in the past)
SELECT
DATE(thisYear.order_date) AS ThisYearOrderDate,
DATE(lastYear.order_date) AS LastYearOrderDate,
YEAR(thisYear.order_date) as ThisYear,
YEAR(lastYear.order_date) as LastYear,
DAYNAME(thisYear.order_date) AS DayName,
SUM(thisYear.total) AS ThisYearTotal,
lastYear.total AS LastYearTotal
FROM orders thisYear
INNER JOIN (
SELECT
order_date as order_date,
SUM(total) as total
FROM orders
GROUP BY order_date
) lastYear
ON DATE_ADD(thisYear.order_date, INTERVAL -52 WEEK) = lastYear.order_date
GROUP BY thisYear.order_date;
以下是它将返回的示例(结果将因存储过程中的随机数据而有所不同):
+--------------+--------------+----------+---------------+---------------+
| ThisYearDate | LastYearDate | DayName | ThisYearTotal | LastYearTotal |
+--------------+--------------+----------+---------------+---------------+
| 2015-01-01 | 2014-01-02 | Thursday | 363.56 | 11.26 |
| 2015-01-02 | 2014-01-03 | Friday | 137.62 | 189.76 |
| 2015-01-03 | 2014-01-04 | Saturday | 399.40 | 257.42 |
| 2015-01-04 | 2014-01-05 | Sunday | 502.80 | 336.38 |
| 2015-01-05 | 2014-01-06 | Monday | 107.59 | 466.79 |
+--------------+--------------+----------+---------------+---------------+
有人能想出一种不同的方法来实现这一目标吗?
修改
我看了一下@Used_By_Already的解决方案并重写了一下,给它的输出与我的表相同:
SELECT
(CASE WHEN order_date < '2014-12-31'
THEN DATE_ADD(order_date, INTERVAL 52 WEEK)
ELSE order_date END) as ThisYearOrderDate,
(CASE WHEN order_date < '2014-12-31'
THEN order_date
ELSE DATE_ADD(order_date, INTERVAL -52 WEEK) END) as LastYearOrderDate,
DAYNAME(order_date) DayName,
SUM(CASE WHEN order_date >= '2014-12-31'
THEN total ELSE 0 END) as ThisYearTotal,
SUM(CASE WHEN order_date < '2014-12-31'
THEN total ELSE 0 END) as LastYearTotal
FROM orders
GROUP BY ThisYearOrderDate;
这很好用,运行速度比我的解决方案快。我唯一担心的是它需要这些日期过滤器,它只能在一年的范围内工作,因为ThisYearOrderDate和LastYearOrderDate之间的任何重叠都会导致一些误导性条目:
+--------------+--------------+-----------+---------------+---------------+
| ThisYearDate | LastYearDate | DayName | ThisYearTotal | LastYearTotal |
+--------------+--------------+-----------+---------------+---------------+
| 2014-12-31 | 2014-01-01 | Wednesday | 18.01 | 253.56 |
| 2015-01-01 | 2014-01-02 | Thursday | 363.56 | 11.26 |
| ... | ... | ... | ... | ... |
| 2015-12-30 | 2014-12-31 | Wednesday | 380.71 | 0.00 |
| 2015-12-31 | 2015-01-01 | Thursday | 400.36 | 0.00 |
+--------------+--------------+-----------+---------------+---------------+
答案 0 :(得分:1)
在此日期和组中使用案例表达式,并对两个总和使用类似的案例表达式(条件聚合)。
select
case when order_date < '2016-01-01' then order_date + INTERVAL 52 WEEKS else order_date end as an_order_date_pair
, SUM(case then order_date < '2016-01-01' then total end) as prev_yr
, SUM(case then order_date >= '2016-01-01' then total end) as this_yr
from orders
group by
case when order_date < '2016-01-01' then order_date + INTERVAL 52 WEEKS else order_date end
我无法在sqlfiddle下运行MySQL,所以使用Postgres完成了以下操作,但你会看到语法非常类似于此需要:
SQL Fiddle PostgreSQL 9.3架构设置:
CREATE TABLE Orders
(order_date timestamp , total decimal(12,3))
;
INSERT INTO Orders
(order_date, total)
VALUES
('2014-01-02 00:00:00', 11.26),
('2014-01-03 00:00:00', 189.76),
('2014-01-04 00:00:00', 257.42),
('2014-01-05 00:00:00', 336.38),
('2014-01-06 00:00:00', 466.79),
('2015-01-01 00:00:00', 363.56),
('2015-01-02 00:00:00', 137.62),
('2015-01-03 00:00:00', 399.40),
('2015-01-04 00:00:00', 502.80),
('2015-01-05 00:00:00', 107.59)
;
查询1 :
select
case when order_date < '2015-01-01'
then order_date + INTERVAL '52 WEEKS'
else order_date
end as an_order_date_pair
, SUM(case when order_date < '2015-01-01'
then total else 0
end) as prev_yr
, SUM(case when order_date >= '2015-01-01'
then total else 0
end) as this_yr
from orders
group by
case when order_date < '2015-01-01'
then order_date + INTERVAL '52 WEEKS'
else order_date
end
<强> Results 强>:
| an_order_date_pair | prev_yr | this_yr |
|---------------------------|---------|---------|
| January, 01 2015 00:00:00 | 11.26 | 363.56 |
| January, 05 2015 00:00:00 | 466.79 | 107.59 |
| January, 02 2015 00:00:00 | 189.76 | 137.62 |
| January, 04 2015 00:00:00 | 336.38 | 502.8 |
| January, 03 2015 00:00:00 | 257.42 | 399.4 |