Вот ответ с использованием всегда интересного Match_Recognize.Обратите внимание, что вы не должны называть столбцы «Код» или «Дата», поскольку они являются зарезервированными ключевыми словами.
Match_Recognize работает в нескольких строках и пытается соответствовать заданному шаблону.В вашем случае вы пытаетесь сопоставить шаблон начального кода, за которым следует ноль или более недопустимых конечных кодов / других кодов, а затем действительный конечный код.
WITH test_vals AS (
SELECT 1 as person_ID,'Start_period' as my_code,to_date('Jan 1','mon dd') as my_date FROM DUAL
UNION ALL SELECT 1,'End_period',to_date('Jan 15','mon dd') FROM DUAL
UNION ALL SELECT 1,'Random_code1',to_date('Feb 15','mon dd') FROM DUAL
UNION ALL SELECT 1,'Random_code2',to_date('Feb 28','mon dd') FROM DUAL
UNION ALL SELECT 1,'End_period',to_date('March 31','mon dd') FROM DUAL
UNION ALL SELECT 1,'Start_period',to_date('May 31','mon dd') FROM DUAL
UNION ALL SELECT 1,'End_period',to_date('June 11','mon dd') FROM DUAL
UNION ALL SELECT 1,'End_period',to_date('October 28','mon dd') FROM DUAL
)
SELECT m.person_id,
m.my_code,
m.my_date,
m.period_id
FROM test_vals t
match_recognize(
PARTITION BY person_id
ORDER BY my_date
MEASURES
match_number() AS period_id /* Return the match number as the period ID */
ALL ROWS PER match
pattern (
start_code /* Match a single start code */
(invalid_end_code | other_code)* /* Match zero or more invalid end codes or other codes */
valid_end_code /* Match a single end code */
)
define
start_code AS my_code = 'Start_period', /* Start codes are always valid */
valid_end_code AS my_code = 'End_period' AND (my_date - FIRST(my_date)) > 28, /* End codes are only valid if they come more than 28 days after the start of the pattern match */
invalid_end_code AS my_code = 'End_period' AND (my_date - FIRST(my_date)) <= 28,
other_code AS my_code NOT IN ('Start_period', 'End_period')
) m