Mysql 按日期范围内的日期聚合数据,结果集中没有日期间隔

Mysql 按日期范围内的日期聚合数据,结果集中没有日期间隔,mysql,sql,time-series,mariadb,Mysql,Sql,Time Series,Mariadb,我有一个销售订单表,我想列出两个日期之间每天销售订单的计数,不留日期间隔 这就是我目前拥有的: SELECT COUNT(*) as Norders, DATE_FORMAT(date, "%M %e") as sdate FROM ORDERS WHERE date <= NOW() AND date >= NOW() - INTERVAL 1 MONTH GROUP BY DAY(date) ORDER BY date ASC; 但我想得到的是: 6 Ma

我有一个销售订单表,我想列出两个日期之间每天销售订单的
计数
,不留日期间隔

这就是我目前拥有的:

SELECT COUNT(*) as Norders, DATE_FORMAT(date, "%M %e") as sdate 
FROM ORDERS 
WHERE date <= NOW() 
  AND date >= NOW() - INTERVAL 1 MONTH 
GROUP BY DAY(date) 
ORDER BY date ASC;
但我想得到的是:

6     May 1
0     May 2
0     May 3
14    May 4
1     May 5
0     May 6
0     May 7
0     May 8
.....
0     Jun 1
8     Jun 2
.....
5     Jun 15
这可能吗?

首先创建一个

选择coalesce(计数(O*),0)作为Norders,DATE_格式(C.DATE,“%M%e”)作为sdate
从日历C
C.date=O.date上的左连接订单O
其中O.date=NOW()-间隔1个月
按天分组(日期)
按日期订购ASC;

动态创建日期范围,并根据订单表加入该范围:-

SELECT sub1.sdate, COUNT(ORDERS.id) as Norders
FROM
(
    SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY), "%M %e") as sdate 
    FROM (SELECT 0 i 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)units
    CROSS JOIN (SELECT 0 i 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)tens
    CROSS JOIN (SELECT 0 i 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)hundreds
    WHERE DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%M %e")
GROUP BY sub1.sdate
这可以处理高达1000天的日期范围

请注意,根据您用于日期的字段类型,可以轻松地提高效率

编辑-根据要求,获取每月订单数量:-

SELECT aMonth, COUNT(ORDERS.id) as Norders
FROM
(
    SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%Y%m") as sdate, DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%M") as aMonth 
    FROM (SELECT 0 i 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 UNION SELECT 10 UNION SELECT 11)months
    WHERE DATE_SUB(NOW(), INTERVAL months.i MONTH) BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%Y%m")
GROUP BY aMonth

您需要生成一个虚拟(或物理)表,其中包含范围内的每个日期

这可以通过使用序列表按如下方式完成

SELECT mintime + INTERVAL seq.seq DAY AS orderdate
  FROM (
        SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
               CURDATE() AS maxtime
          FROM obs
       ) AS minmax
  JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
你可以在网站上找到关于这一切的详细解释


如果您使用的是MariaDB版本10+,这些序列表是内置的。

您有包含所有日期的日历表吗?@GordonLinoff否,我只有ORDERs表,其中只包含创建订单日期的行。@oliakaoil是的,完全正确。这就是重点。
ORDERS
表格不包含每天的行,而只包含创建订单的日期。这需要有一个
日历
表格,该表格在每年的每个月都有每天的行,对吗?@alexandernst是的,请按照答案中的链接进行操作,效率不高,IMHO。如果没有其他办法,我会把它作为B计划。@alexandernst我建议你对理货/数字/序列表的使用进行一些研究。RegardsNice技巧:)只有一个错误,它返回
1
而不是
0
我没有任何销售订单的日期。不应该这样做,但它确实需要COUNT(…)计算orders表(我假设id)中的一列,当没有订单时,该列的值将为NULL(COUNT和列名将忽略NULL值)。如果您使用COUNT(*),它会将没有订单的行计数为1。啊,的确如此。你说得对。我只是写了*而不是
id
。谢谢你,这很好用!就同一个话题再问一个问题。如果我想显示过去12个月每月订单的
计数
,我应该如何修改
sub1
子选项?增加了一个建议。+1指出MariaDB 10+内置了序列表。
SELECT aMonth, COUNT(ORDERS.id) as Norders
FROM
(
    SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%Y%m") as sdate, DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%M") as aMonth 
    FROM (SELECT 0 i 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 UNION SELECT 10 UNION SELECT 11)months
    WHERE DATE_SUB(NOW(), INTERVAL months.i MONTH) BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%Y%m")
GROUP BY aMonth
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
  FROM (
        SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
               CURDATE() AS maxtime
          FROM obs
       ) AS minmax
  JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
SELECT IFNULL(orders.Norders,0) AS Norders, /* show zero instead of null*/
       DATE_FORMAT(alldates.orderdate, "%M %e") as sdate 
  FROM (
        SELECT mintime + INTERVAL seq.seq DAY AS orderdate
          FROM (
                SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
                       CURDATE() AS maxtime
                  FROM obs
               ) AS minmax
          JOIN seq_0_to_999999 AS seq 
                        ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
       ) AS alldates
  LEFT JOIN (
    SELECT COUNT(*) as Norders, DATE(date) AS orderdate
      FROM ORDERS 
    WHERE date <= NOW() 
      AND date >= NOW() - INTERVAL 1 MONTH 
    GROUP BY DAY(date) 
       ) AS orders ON alldates.orderdate = orders.orderdate
ORDER BY alldates.orderdate ASC
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;

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

DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
  FROM seq_0_to_999 a
  JOIN seq_0_to_999 b
);