Home > Enterprise >  SQL Query to find the first occurence of the date change
SQL Query to find the first occurence of the date change

Time:12-04

I have changed the question to make it more understandable

I have created a query to get the following output with date:

PERSON_nUMBER  base_element_name  effective_start_date  effective_end_date  Value   
           11  Health             2021-11-21            4712-12-31            5.5
           11  Health             2021-10-18            2021-11-20            5.5
           12  Health             2021-11-11            4712-12-31              0
           12  Health             2021-11-01            2021-11-10            5.5
           13  Health             2021-11-01            2021-11-09            6
           
           14 Health              2021-11-10            2021-11-19            6
           14  Health             2021-11-20            2021-11-30            6
           14  Health             2021-12-01            2021-12-05            5
           14  Health             2021-12-06            4712-12-31            5
           
           
           15  Health             2021-11-10           2021-11-19           6
           15  Health             2021-11-20            2021-11-30           6
           15  Health             2021-12-01            2021-12-05           6
           15  Health              2021-12-06            2021-12-15           5
           15  Health              2021-12-16            4712-12-31           5

       
       

I pass the parameter as 01-11-2021 and 30-11-2021

select 
    petf.person_number
    petf.base_element_name,
    to_char(peef.effective_start_date,'YYYY-MM-DD') effective_start_date ,
     to_char(peef.effective_end_date,'YYYY-MM-DD') effective_end_date,
     pivf.value  
    from pay_element_types_f petf ,
    pay_input_values_f pivf,
    per_all_elementries_f peef
    where pivf.element_type_id = petf.element_type_id
    AND petf.base_element_name in ('Health')
    and peef.element_entry_id= pivf.element_entry_id
    and peef.effective_start_date between :from_date and :to_date

How can I tweak the above query such that if there is a change in value column like person_number 12 then the two rows should come in output; and if there is no change within the two rows of the same employee like person_number 11 then only the latest row should be picked.

The expected output is -

PERSON_nUMBER  base_element_name  effective_start_date    Value   
           11  Health             2021-11-21               5.5
           12  ~Health            2021-11-11               0
           12  Health             2021-11-01              5.5
           13  Health             2021-11-01               6
           14  Health             2021-11-10               6 
           14  ~Health             2021-12-01               5
           15  Health             2021-11-10               6
           15  ~Health             2021-12-06               5

    

Case

  1. if there is a change in value from 5.5 to 0 for person_number 12 then both the rows should come and latest one base_element_name should have "~" added.
  2. If there is Health tagged to an employee only once and is end date then only that row should be displayed.
  3. If there is no change in the value within the date range then the latest row should come for example-11
  4. For person#14 and 15 , the first effective start date should come and the first occurence effective start date of the "latest" change or the last changed value

CodePudding user response:

Just rank your query to show the 2 latest dates then add ~ to the 2nd latest date. Hope this could help.

WITH CTE AS(
select 
    petf.person_number
    petf.base_element_name,
    to_char(peef.effective_start_date,'YYYY-MM-DD') effective_start_date ,
     to_char(peef.effective_end_date,'YYYY-MM-DD') effective_end_date,
     pivf.value  
    ,RANK() OVER(PARTITION BY petf.person_number ORDER BY peef.effective_start_date desc) as my_rank --Add Rank  to Get the 2 latest date.
    from pay_element_types_f petf ,
    pay_input_values_f pivf,
    per_all_elementries_f peef
    where pivf.element_type_id = petf.element_type_id
    AND petf.base_element_name in ('Health')
    and peef.element_entry_id= pivf.element_entry_id
    and peef.effective_start_date between :from_date and :to_date
)

SELECT 
person_number
,CASE WHEN my_rank = 2 THEN CONCAT('~',base_element_name) ELSE base_element_name END as base_element_name
,effective_start_date
,Value
FROM CTE
WHERE my_rank in (1,2)

CodePudding user response:

From Oracle 12, you can use MATCH_RECOGNIZE:

SELECT person_number,
       CASE cls WHEN 'CHANGED' THEN '~' END || base_element_name
         AS base_element_name,
       effective_start_date,
       effective_end_date,
       value
FROM   (
  SELECT *
  FROM   your_query
  -- Without date filter
)
MATCH_RECOGNIZE (
  PARTITION BY person_number
  ORDER     BY effective_start_date
  MEASURES
    CLASSIFIER() AS cls
  ALL ROWS PER MATCH
  PATTERN (first_row (changed | {- outside_range -} | $))
  DEFINE
   first_row     AS effective_start_date <  :to_date   INTERVAL '1' DAY
                AND effective_end_date   >= :from_date,
   changed       AS value != first_row.value,
   outside_range AS effective_start_date >= :to_date   INTERVAL '1' DAY
);

Which, for the sample data:

CREATE TABLE your_query (
  PERSON_NUMBER,
  base_element_name,
  effective_start_date,
  effective_end_date,
  Value
) AS
SELECT 11, 'Health', DATE '2021-11-21', DATE '4712-12-31', 5.5 FROM DUAL UNION ALL
SELECT 11, 'Health', DATE '2021-10-18', DATE '2021-11-20', 5.5 FROM DUAL UNION ALL
SELECT 12, 'Health', DATE '2021-11-11', DATE '4712-12-31', 0.0 FROM DUAL UNION ALL
SELECT 12, 'Health', DATE '2021-11-01', DATE '2021-11-10', 5.5 FROM DUAL UNION ALL
SELECT 13, 'Health', DATE '2021-11-01', DATE '2021-11-09', 6.0 FROM DUAL UNION ALL
SELECT 14, 'Health', DATE '2021-11-10', DATE '2021-11-19', 6.0 FROM DUAL UNION ALL
SELECT 14, 'Health', DATE '2021-11-20', DATE '2021-11-30', 6.0 FROM DUAL UNION ALL
SELECT 14, 'Health', DATE '2021-12-01', DATE '2021-12-05', 5.0 FROM DUAL UNION ALL
SELECT 14, 'Health', DATE '2021-12-06', DATE '4712-12-31', 5.0 FROM DUAL UNION ALL
SELECT 15, 'Health', DATE '2021-11-10', DATE '2021-11-19', 6.0 FROM DUAL UNION ALL
SELECT 15, 'Health', DATE '2021-11-20', DATE '2021-11-30', 6.0 FROM DUAL UNION ALL
SELECT 15, 'Health', DATE '2021-12-01', DATE '2021-12-05', 6.0 FROM DUAL UNION ALL
SELECT 15, 'Health', DATE '2021-12-06', DATE '2021-12-15', 5.0 FROM DUAL UNION ALL
SELECT 15, 'Health', DATE '2021-12-16', DATE '4712-12-31', 5.0 FROM DUAL UNION ALL
SELECT 16, 'Health', DATE '2021-10-16', DATE '2021-11-15', 4.0 FROM DUAL UNION ALL
SELECT 16, 'Health', DATE '2021-11-16', DATE '4712-12-31', 5.0 FROM DUAL;

(Which includes person #16 where the effective_start_date of the first row is before the start of the range but the change occurs within the range.)

Outputs:

PERSON_NUMBER BASE_ELEMENT_NAME EFFECTIVE_START_DATE EFFECTIVE_END_DATE VALUE
11 Health 2021-11-21 00:00:00 4712-12-31 00:00:00 5.5
12 Health 2021-11-01 00:00:00 2021-11-10 00:00:00 5.5
12 ~Health 2021-11-11 00:00:00 4712-12-31 00:00:00 0
13 Health 2021-11-01 00:00:00 2021-11-09 00:00:00 6
14 Health 2021-11-20 00:00:00 2021-11-30 00:00:00 6
14 ~Health 2021-12-01 00:00:00 2021-12-05 00:00:00 5
15 Health 2021-11-20 00:00:00 2021-11-30 00:00:00 6
16 Health 2021-10-16 00:00:00 2021-11-15 00:00:00 4
16 ~Health 2021-11-16 00:00:00 4712-12-31 00:00:00 5

db<>fiddle here

  • Related