2014-12-11 65 views
0

我正在构建查询以返回每日销售数据。我目前的查询将返回类似于下面的表格:MySQL报告 - 填写空日期

---------------------------------- 
| DATE  | SKU  | TOTAL | 
---------------------------------- 
| 2014-11-01 | AV155_A | 209.00 | 
| 2014-11-02 | AV155_B | 627.00 | 
| 2014-11-04 | AV155_C | 279.00 | 
| 2014-11-05 | AV155 | 279.00 | 
| 2014-11-08 | AV1556_A | 209.00 | 
| 2014-11-09 | AV1556_B | 627.00 | 
| 2014-11-10 | AV1556_C | 279.00 | 
| 2014-11-12 | AV1556 | 279.00 | 

我想是一个成绩表显示每一天,即使有该日期没有数据点。事情是这样的:

---------------------------------- 
| DATE  | SKU  | TOTAL | 
---------------------------------- 
| 2014-11-01 | AV155_A | 209.00 | 
| 2014-11-02 | AV155_B | 627.00 | 
| 2014-11-03 |   |  0 | 
| 2014-11-04 | AV155_C | 279.00 | 
| 2014-11-05 | AV155 | 279.00 | 
| 2014-11-06 |   |  0 | 
| 2014-11-07 |   |  0 | 
| 2014-11-08 | AV1556_A | 209.00 | 
| 2014-11-09 | AV1556_B | 627.00 | 
| 2014-11-10 | AV1556_C | 279.00 | 
| 2014-11-11 |   |  0 | 
| 2014-11-12 | AV1556 | 279.00 | 

查询我目前有看起来像这样:

select 
    DATE_FORMAT(created_on, '%m-%d-%Y') as date, 
    sku, 
    SUM(price) as total 
FROM order_items 
WHERE created_on between FROM_UNIXTIME(1415577600) AND NOW() 
GROUP BY MONTH(created_on), DAY(v.created_on), order_item_sku; 
+0

我相信要做到这一点,你需要执行一个UPDATE插入所需范围内没有数据的日期行,或者使用SQL的流控制和编程,即创建一个循环访问日期范围的存储过程。没有看到如何在简单的SELECT查询中做到这一点... – RobP 2014-12-11 01:15:46

回答

2

您需要使用外连接。最简单的方法是,如果你有一个日历表,但你可以做一个在飞行:

select c.thedate, oi.sku, sum(price) as total 
from (select date('2014-11-01') as thedate union all 
      date('2014-11-02') as thedate union all 
      date('2014-11-03') as thedate union all 
      date('2014-11-04') as thedate union all 
      date('2014-11-05') as thedate union all 
      date('2014-11-06') as thedate union all 
      date('2014-11-07') as thedate union all 
      date('2014-11-08') as thedate union all 
      date('2014-11-09') as thedate union all 
      date('2014-11-10') as thedate union all 
      date('2014-11-11') as thedate union all 
      date('2014-11-12') as thedate 
    ) c left join 
     order_items oi 
     on c.thedate = date(oi.created_on) 
where oi.created_on between FROM_UNIXTIME(1415577600) AND NOW() 
group by ci.thedate, oi.sku 
+0

嗯。是的,我将在我的API中使用这个查询,所以我需要能够动态生成查询。我将如何去使用日历表?我还看到了有关使用其他表的ID来动态生成一系列日期的信息。 – 2014-12-11 01:20:35

+0

@JoelBrewer。 。 。如果您是通过API来完成这项工作的,那么您不能只在应用程序层中添加额外的行吗? – 2014-12-11 02:03:18

1

下面是解决了日期的灵活列表需要一个答案。您需要找出一种方法来获取包含适当范围内所有日期的虚拟表格,然后将它们加入摘要。这是一个查询,将获得该范围内的日期。

SELECT mintime + INTERVAL seq.seq DAY AS reportdate 
    FROM (
     SELECT MIN(DATE(created_on)) AS mintime, 
       MAX(DATE(created_on)) AS maxtime 
      FROM order_items 
     WHERE created_on >= starting_time 
      AND created_on <= NOW() 
     ) AS order_items 
    JOIN seq_0_to_999 AS seq 
        ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime) 

这是怎么回事?三件事。

  1. 我们有一个子查询,它决定我们关心报告的第一天和最后一天(最小和最大created_on)。

  2. 我们将时间范围应用于该查询。我喜欢避免使用BETWEEN作为时间戳范围,因为它经常在错误的一秒错误中得到错误的结束时间。

  3. 我们有一个名为seq_0_to_999的表。它包含一千个基数的序列:从零开始的整数。更多关于这一点。

然后,您可以将其作为子查询加入到您的聚合查询中,以获取所列范围中的所有日期,如此。

select DATE_FORMAT(d.reportdate, '%m-%d-%Y') as date, 
     sku, 
     SUM(price) as total 
    FROM (
      SELECT mintime + INTERVAL seq.seq DAY AS reportdate 
      FROM (
        SELECT MIN(DATE(created_on)) AS mintime, 
         MAX(DATE(created_on)) AS maxtime 
        FROM order_items 
        WHERE created_on >= starting_time 
        AND created_on <= NOW() 
       ) AS order_items 
      JOIN seq_0_to_999 AS seq 
           ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime) 
     ) AS d 
    LEFT JOIN order_items ON d.reportdate = DATE(order_items.created_on)  
    WHERE created_on >= starting_time 
    AND created_on <= NOW() 
    GROUP BY d.reportdate, sku 
    ORDER BY d.reportdate, sku 

它看起来像一个查询大讨厌的毛球。但是,如果您将其视为由各种查询层组成的三明治,则确实不那么复杂。

它使用LEFT JOIN,因此它确保即使您的order_items表中没有相应的数据,该范围内的所有日期都会保留。

最后,这个seq_0_to_999表怎么办?我们从哪里获得从零开始的整数?答案是这样的:我们必须安排这样做;这些数字不是内置在MySQL中的。 (它们被构建到名为MariaDB的MySQL分支中。)在其0-9创建一个整数短表,就像这样:

DROP TABLE IF EXISTS seq_0_to_9; 
CREATE TABLE seq_0_to_9 AS 
    SELECT 0 AS seq UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 
    UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9; 

然后创建一个连接该表与自身,以便产生1000个组合是这样的:

DROP VIEW IF EXISTS seq_0_to_999; 
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq 
    FROM seq_0_to_9 a 
    JOIN seq_0_to_9 b 
    JOIN seq_0_to_9 c 
); 

我在这里写一些详细的内容在http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/

+0

嗯。这看起来非常好,我认为我真的很接近,但由于某种原因,它仍然没有填补缺失的日期.. – 2014-12-11 03:16:22