【问题标题】:Show all data in a date range using MYSQL recursive function使用MYSQL递归函数显示日期范围内的所有数据
【发布时间】:2020-05-01 01:11:08
【问题描述】:

我正在尝试获取过去 6 个月的销售额列表,如果我没有特定月份的数据,则获取 0 值。所以我使用recursive_all_dates 生成过去 6 个月的日期范围,效果很好:

with recursive all_dates(dt) as (
    -- anchor
    select DATE_SUB(now(), INTERVAL 6 MONTH) dt
        union all
    -- recursion with stop condition
    select dt + interval 1 month from all_dates where dt + interval 1 month <= DATE(now())
)
select DATE_FORMAT(dt, '%Y-%m') as ym from all_dates

这将返回:

ym
------
2019-10
2019-11
2019-12
2020-01
2020-02
2020-03
2020-04

现在我想用我的真实数据加入这个:

with recursive all_dates(dt) as (
    -- anchor
    select DATE_SUB(now(), INTERVAL 6 MONTH) dt
        union all 
    -- recursion with stop condition
    select dt + interval 1 month from all_dates where dt + interval 1 month <= now()
)
SELECT
    DATE_FORMAT(ad.dt, '%Y-%m') as ym,
    sum(profit) as profit
FROM
    all_dates as ad
LEFT JOIN organisation_invoices as i
ON
    DATE_FORMAT(ad.dt, '%Y-%m') = DATE_FORMAT(i.issue_date, '%Y-%m')
JOIN (
    SELECT
        invoice_id,
        SUM(value) as profit
    FROM organisation_invoice_services isrv
    GROUP BY invoice_id
) isrv
ON i.id = isrv.invoice_id
WHERE
    i.organisation_id = '4b166dbe-d99d-5091-abdd-95b83330ed3a' AND
    i.issue_date >= DATE_SUB(NOW(), INTERVAL 6 MONTH)
GROUP BY `ym`
ORDER BY `ym` ASC

但我仍然只得到人口稠密的月份:

ym         profit
------------------
2019-12    8791
2020-02    302
2020-04    10452

想要的结果:

ym         profit
------------------
2019-10    0
2019-11    0
2019-12    8791
2020-01    0
2020-02    302
2020-03    0
2020-04    10452

我错过了什么?

编辑:示例数据集和小提琴:

CREATE TABLE `organisation_invoices` (
  `id` varchar(255) NOT NULL,
  `organisation_id` varchar(255) NOT NULL,
  `issue_date` date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `organisation_invoice_services` (
  `id` varchar(255) NOT NULL,
  `organisation_id` varchar(255) NOT NULL,
  `invoice_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `qty` float NOT NULL,
  `value` float NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `organisation_invoices` (id, organisation_id, issue_date)
VALUES ('e11cec69-138f-4e20-88e5-5430b6c8d0a1', '4b166dbe-d99d-5091-abdd-95b83330ed3a', '2020-01-20');

INSERT INTO `organisation_invoice_services` (id, organisation_id, invoice_id, qty, `value`)
VALUES ('fe45dfd67-138f-4e20-88e5-5430b6c8d0a1', '4b166dbe-d99d-5091-abdd-95b83330ed3a', 'e11cec69-138f-4e20-88e5-5430b6c8d0a1', 1, 1000);

https://www.db-fiddle.com/f/dibyQi31CBtr2Cr8vjJA8i/0

【问题讨论】:

  • 我更新了我的问题以包含一个工作小提琴

标签: mysql


【解决方案1】:

您可以使用以下内容:

with recursive all_dates(dt) as (
    -- anchor
    select DATE_SUB(now(), INTERVAL 6 MONTH) dt
    union all 
    -- recursion with stop condition
    select dt + interval 1 month from all_dates where dt + interval 1 month <= now()
)
SELECT DATE_FORMAT(ad.dt, '%Y-%m') as ym, IFNULL(sum(profit),0) as profit
FROM all_dates as ad
LEFT JOIN organisation_invoices as i
ON DATE_FORMAT(ad.dt, '%Y-%m') = DATE_FORMAT(i.issue_date, '%Y-%m')
LEFT JOIN (
  SELECT
  invoice_id,
  SUM(value) as profit
  FROM organisation_invoice_services isrv
  GROUP BY invoice_id
) isrv
ON i.id = isrv.invoice_id
WHERE
    (i.organisation_id = '4b166dbe-d99d-5091-abdd-95b83330ed3a' AND
    i.issue_date >= DATE_SUB(NOW(), INTERVAL 6 MONTH)) OR i.organisation_id IS NULL
GROUP BY `ym`
ORDER BY `ym` ASC

demo on dbfiddle.uk

变化:

WHERE 子句中的条件会改变 LEFT JOIN 的行为。由于您检查特定的organization_id,因此您只能在月份表和数据之间获得匹配(LEFT JOIN 的行为类似于INNER JOIN)。您需要以下 WHERE 子句:

WHERE (i.organisation_id = '4b166dbe-d99d-5091-abdd-95b83330ed3a' AND
  i.issue_date >= DATE_SUB(NOW(), INTERVAL 6 MONTH)) OR i.organisation_id IS NULL

您还必须将第二个 JOIN 更改为 LEFT JOIN

【讨论】:

  • 我试过这个,我得到了同样的结果。我还编辑了我的问题以添加一个有效的小提琴链接。
猜你喜欢
  • 1970-01-01
  • 2013-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多