Показать все данные в диапазоне дат, используя рекурсивную функцию MYSQL - PullRequest
1 голос
/ 30 апреля 2020

Я пытаюсь получить список продаж за последние 6 месяцев и получить 0 значений, если у меня нет данных за определенный c месяц. Поэтому я использую 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

1 Ответ

0 голосов
/ 30 апреля 2020

Вы можете использовать следующее:

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

демо на dbfiddle.uk

Изменения:

Условия в предложении WHERE изменяют поведение вашего LEFT JOIN. Поскольку вы проверяете заданный c 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...