У меня есть таблица со следующими строками:
| item_id | change_type | change_date | change_id | other columns...
| :------ | :---------- | :---------- | :-------- |
| 123 | off | 2019-06-04 | 321 |
| 123 | on | 2019-07-11 | 741 |
| 123 | off | 2019-07-13 | 987 |
| 123 | on | 2019-08-01 | 951 |
| 123 | off | 2019-08-07 | 357 |
| 456 | off | 2019-08-01 | 125 |
| 456 | on | 2019-11-18 | 878 |
| 789 | on | 2019-12-18 | 373 |
| 012 | off | 2019-12-25 | 654 |
| 698 | off | 2019-08-01 | 741 |
| 698 | on | 2018-01-03 | 147 |
Я пытаюсь выполнить запрос, который дает следующий результат:
| item_id | on_date | off_date | on_id | off_id | other columns...
| :------ | :--------- | :--------- | :---- | :----- |
| 123 | | 2019-06-04 | | 321 |
| 123 | 2019-07-11 | 2019-07-13 | 741 | 987 |
| 123 | 2019-08-01 | 2019-08-07 | 951 | 357 |
| 456 | | 2019-08-01 | | 125 |
| 456 | 2019-11-18 | | 878 | |
| 789 | 2019-12-18 | | 373 | |
| 012 | | 2019-12-25 | | 654 |
| 698 | 2018-01-03 | 2019-08-01 | 147 | 741 |
Нужный результат представляет собой таблицу, в которой даты «включено» и даты «выключено» отмечены в порядке убывания (сгруппированы по item_id
), причем даты «выключено» в той же строке, что и предыдущая (по времени) дата «вкл.»
Наиболее близкими мне были следующие варианты:
Попытка первая:
SELECT
changes_main.item_id,
`on_date`,
`off_date`,
`on_id`,
`off_id`
FROM (
SELECT DISTINCT `item_id`
FROM item_changes
) AS changes_main
LEFT OUTER JOIN (
SELECT
`item_id`, -- for joining purposes only
`change_date` AS `on_date`,
`change_id` AS `on_id`
FROM item_changes
WHERE `change_type` = 'on'
) AS changes_ons ON changes_ons.item_id = changes_main.item_id
RIGHT OUTER JOIN ( -- although LEFT or RIGHT doesn't seem to matter
SELECT
`item_id`, -- for joining purposes only
`change_date` AS `off_date`,
`change_id` AS `off_id`
FROM item_changes
WHERE `change_type` = 'off'
) AS changes_offs ON changes_offs.item_id = changes_main.item_id
;
Однако это, по сути, приводит к CROSS JOIN
между on_date
и off_date
.
Единственное изменение во второй попытке - добавить предложение WHERE
. Это была идея, которую я получил от этого вопроса .
Попытка вторая:
-- Same exact query as the above, however with the following
-- WHERE statement placed where the semicolon is above:
WHERE
`off_date` = (
SELECT MIN(offs2.change_date)
FROM item_changes AS offs2
WHERE offs2.change_type = 'off' AND
offs2.change_date > changes_ons.on_date
)
;
Проблема в том, что там, где есть четное число " on / off »в item_id, эти дополнительные« on »или« off »отфильтровываются.
Я пробовал вариант вышеуказанного WHERE
предложения, включая OR off_date IS NULL
, OR on_date IS NULL
, et c.
Обновление:
Третьей попыткой было использование UNION
и некоторых SCALAR SUBQUERIES
. Это был самый близкий мне результат, который мне нужен. Тем не менее, по-прежнему не хватает (например, он не включает change_id
, а также не создает идеального соответствия).
SELECT
changes_on.item_id,
changes_on.change_date AS `on_date`,
(SELECT MIN(offs2.change_date)
FROM item_changes AS offs2
WHERE offs2.change_type = 'off' AND
offs2.change_date > changes_ons.change_date
) AS `off_date`,
changes_on.change_id AS `on_id`,
NULL AS `off_id` -- odd
FROM item_changes AS changes_on
WHERE `change_type` = 'on'
UNION
SELECT
changes_offs.item_id,
changes_offs.change_date AS `off_date`,
(SELECT MIN(ons2.change_date)
FROM item_changes AS ons2
WHERE ons2.change_type = 'on' AND
ons2.change_date < changes_offs.on_date
) AS `off_date`,
NULL AS `on_id`, -- odd
changes_offs.change_id AS `off_id`
FROM item_changes AS changes_offs
WHERE `change_type` = 'off'
;
Ассистент / ввод / руководство приветствуются.