根据一列中的值创建时间间隔 / SQL Oracle

Create time intervals based on values in one column / SQL Oracle

我需要创建从 table 开始 return 时间间隔的查询,它具有(几乎)每天的属性。
原来的 table 如下所示:

Person | Date       | Date_Type
-------|------------|----------
Sam    | 01.06.2020 |  Vacation
Sam    | 02.06.2020 |  Vacation
Sam    | 03.06.2020 |  Work
Sam    | 04.06.2020 |  Work
Sam    | 05.06.2020 |  Work
Frodo  | 01.06.2020 |  Work
Frodo  | 02.06.2020 |  Work
.....

所需的应该如下所示:

Person | Date_Interval         | Date_Type
-------|-----------------------|----------
Sam    | 01.06.2020-02.06.2020 |  Vacation
Sam    | 03.06.2020-05.06.2020 |  Work
Frodo  | 01.06.2020-02.06.2020 |  Work
.....

如有任何想法将不胜感激:)

这是一种 gaps-and-islands 问题。

要获得具有相同 date_type 的相邻日期,您可以减去一个序列。相邻几天它将保持不变。然后可以聚合:

select person, date_type, min(date), max(date)
from (select t.*,
             row_number() over (partition by person, date_type
                                             order by date) as seqnum
      from t
     ) t
group by person, date_type, (date - seqnum);

这看起来像是一个 gaps-and-island 问题。这是一种方法:

select person, min(date) startdate, max(date) enddate, date_type
from (
    select t.*,
        row_number() over(partition by person order by date) rn1,
        row_number() over(partition by person, date_type order by date) rn2
    from mytable t
) t
group by person, date_type, rn1 - rn2

如果不是所有日期都是连续的,这也有效(因为你说你有 几乎 所有日期,我知道你没有所有日期)。

最简单的方法之一是使用 MATCH_RECOGNIZE 执行 row-by-row 比较和聚合:

SELECT *
FROM   table_name
MATCH_RECOGNIZE (
  PARTITION BY Person
  ORDER     BY "DATE"
  MEASURES
    FIRST( "DATE" )    AS start_date,
    LAST( "DATE")      AS end_date,
    FIRST( Date_Type ) AS date_type
  ONE ROW PER MATCH
  PATTERN ( successive_dates+ )
  DEFINE
    SUCCESSIVE_DATES AS (
          FIRST( Date_Type ) = NEXT( Date_Type )
      AND MAX( "DATE" ) + INTERVAL '1' DAY = NEXT( "DATE")
    )
);

其中,对于示例数据:

CREATE TABLE table_name ( Person, "DATE", Date_Type ) AS
SELECT 'Sam',   DATE '2020-06-01', 'Vacation' FROM DUAL UNION ALL
SELECT 'Sam',   DATE '2020-06-02', 'Vacation' FROM DUAL UNION ALL
SELECT 'Sam',   DATE '2020-06-03', 'Work' FROM DUAL UNION ALL
SELECT 'Sam',   DATE '2020-06-04', 'Work' FROM DUAL UNION ALL
SELECT 'Sam',   DATE '2020-06-05', 'Work' FROM DUAL UNION ALL
SELECT 'Frodo', DATE '2020-06-01', 'Work' FROM DUAL UNION ALL
SELECT 'Frodo', DATE '2020-06-02', 'Work' FROM DUAL;

输出:

PERSON | START_DATE          | END_DATE            | DATE_TYPE
:----- | :------------------ | :------------------ | :--------
Frodo  | 2020-06-01 00:00:00 | 2020-06-01 00:00:00 | Work     
Sam    | 2020-06-01 00:00:00 | 2020-06-01 00:00:00 | Vacation 
Sam    | 2020-06-03 00:00:00 | 2020-06-04 00:00:00 | Work     

db<>fiddle here