MySQL报告 - 填写空日期

时间:2014-12-11 01:13:46

标签: mysql sql

我正在构建一个查询以返回每日销售数据。我当前的查询返回一个类似于这样的表:

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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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的表。它包含一千个基数的序列:从零开始的整数。稍等一下。

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

    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/

    详细地写了这篇文章