Reputation: 47
I want to count the number of records partitioned by a group and ordered by a date but with certains conditions, in this case that two column values must both be higher than the current row compared to previous rows.
(
select 'a' grouper,5 num1,55 num2,to_date('01.01.2022','DD.MM.YYYY') as dt,null as expected_res from dual union all
select 'a' grouper,3 num1,40 num2,to_date('02.01.2022','DD.MM.YYYY') as dt,1 as expected_res from dual union all
select 'a' grouper,4 num1,100 num2,to_date('03.01.2022','DD.MM.YYYY') as dt,null as expected_res from dual
)
select base.* from base order by grouper,dt;
grouper | num1 | num2 | dt | expected |
---|---|---|---|---|
a | 5 | 55 | 01.01.2022 | null (For this partition there is now previous record order by date) |
a | 3 | 40 | 02.01.2022 | 1 (For this partition there is a previous record and both num1 and num2 are eacher higher than those of this row |
a | 4 | 100 | 03.01.2022 | null (For this partition there are two previous records but in the previous row [row2] both nums are lower so i dont want to count this row and for the first row num1 is higher than num1 of this row but num2 is lower, so i dont want to count it |
I know that i can use a corrected subquery for this problem but the performance will be bad due to high amount of data. Is this problem solvable by using window functions?
I would need something like this:
count(*) over (partition by grouper order by dt asc rows between unbounded preceding and 1 preceding where num1 AND num2 in window > num1 AND num2 of current row
Upvotes: 0
Views: 402
Reputation: 7786
Maybe you could try to use MODEL clause. It is supposed to be fast.
WITH -- Sample data
base AS
(
Select 'a' "GROUPER", 5 "NUM1", 55 "NUM2", To_Date('01.01.2022','DD.MM.YYYY') "DT" From Dual Union All
Select 'a', 3, 40, To_Date('02.01.2022','DD.MM.YYYY') From Dual Union All
Select 'a', 4, 100, To_Date('03.01.2022','DD.MM.YYYY') From Dual
)
M a i n S Q L
Select GROUPER, NUM1, NUM2, DT, EXPECTED_RESULT
From
( Select ROWNUM "RN",GROUPER, NUM1, NUM2, DT, 0 "EXPECTED_RESULT"
From base
Order By DT
)
MODEL Partition By (GROUPER)
Dimension By (RN)
Measures (NUM1, NUM2, DT,EXPECTED_RESULT)
RULES ( EXPECTED_RESULT[ANY] = CASE WHEN CV(RN) = 1 THEN Null
ELSE
CASE WHEN NUM1[CV() - 1] > NUM1[CV()] And NUM2[CV() - 1] > NUM2[CV()] THEN 1 END
END
)
R e s u l t :
GROUPER NUM1 NUM2 DT EXPECTED_RESULT
------- ---------- ---------- --------- ---------------
a 5 55 01-JAN-22
a 3 40 02-JAN-22 1
a 4 100 03-JAN-22
Updated:
The above code compares values with 1 previous row only. If you want to compare values in current row with any of the previous rows and count those that has both values greater than in current row - check the code below. Notice that the sample data is changed a bit.
WITH
base AS
(
Select 'a' "GROUPER", 5 "NUM1", 57 "NUM2", To_Date('01.01.2022','DD.MM.YYYY') "DT" From Dual Union All
Select 'a', 3, 56, To_Date('02.01.2022','DD.MM.YYYY') From Dual Union All
Select 'a', 2, 51, To_Date('03.01.2022','DD.MM.YYYY') From Dual Union All
Select 'a', 3, 50, To_Date('04.01.2022','DD.MM.YYYY') From Dual Union All
Select 'a', 4, 55, To_Date('05.01.2022','DD.MM.YYYY') From Dual
)
Select GROUPER, NUM1, NUM2, DT, EXPECTED_RESULT
From ( Select ROWNUM "RN", GROUPER, NUM1, NUM2, DT, 0 "EXPECTED_RESULT"
From base
Order By DT
)
MODEL Partition By (GROUPER)
Dimension By (RN, NUM1, NUM2)
Measures (DT, EXPECTED_RESULT)
RULES ( EXPECTED_RESULT[ANY, ANY, ANY] = Count(DT)[RN < CV(RN), NUM1 > CV(NUM1), NUM2 > CV(NUM2) ] )
GROUPER NUM1 NUM2 DT EXPECTED_RESULT
------- ---------- ---------- --------- ---------------
a 5 57 01-JAN-22 0
a 3 56 02-JAN-22 1
a 2 51 03-JAN-22 2
a 3 50 04-JAN-22 1
a 4 55 05-JAN-22 1
Upvotes: 1