【发布时间】:2020-03-18 20:23:24
【问题描述】:
我有一个包含以下行的表格:
| 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 |
我需要的结果是一个表格,其中日期“on”和“off”日期按降序记录(按
item_id分组),“off”日期与前一个日期在同一行(按时间) “开”日期。
我得到的最接近的是以下变体:
尝试一:
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
;
但是,这实质上会在on_date 和off_date 之间产生一个CROSS JOIN。
第二次尝试的唯一变化是添加WHERE 子句。这是我从this question 那里得到的一个想法。
尝试二:
-- 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
)
;
问题在于,如果 item_id 中有非偶数的“on/off”,多余的“on”或“off”会被过滤掉。
我已经尝试过上述WHERE 子句的变体,包括OR off_date IS NULL、OR on_date IS NULL 等。
更新:
第三次尝试是使用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'
;
我们将不胜感激助理/输入/指导。
【问题讨论】:
-
易于使用的窗口函数,在 MySQL 8.x 中可用。您使用的是 MySQL 5.x 还是 8.x?
-
不幸的是 5.x。目前。