SQL查询以获取ID中的日期对

问题描述 投票:0回答:1

我有一个包含以下行的表:

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

但是,这实际上在CROSS JOINon_date之间实现了off_date

第二次尝试的唯一变化是添加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 NULLOR 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 sql join self-join
1个回答
0
投票

根据每行之前的“开”数量分配一个组。然后使用条件聚合:

select item_id,
       max(case when change_type = 'on' then date end) as on_date,
       max(case when change_type = 'on' then change_id end) as on_change_id,
       max(case when change_type = 'off' then date end) as off_date,
       max(case when change_type = 'off' then change_id end) as off_change_id
from (select t.*,
             sum(case when change_type = 'on' then 1 else 0 end) over (partition by item_id order by change_date) as grp
      from t
     ) t
group by item_id, grp;
© www.soinside.com 2019 - 2024. All rights reserved.