SQL query to get previous and new column detail from a table - sql

I have a table that has both audit data for addresses-
ADDRESS_ID EFFECTIVE_START_DATE EFFECTIVE_END_DATE ADDRESS_LINE_1 ADDRESS_LINE_2 TOWN Person_id
300000009360612 2020-04-01 4712-12-31 Box 14 ADAM SELLERS ST Mirror 157
300000009360612 2000-03-27 2020-03-31 Box 13 Mirror 157
I want the previous value and current value for the columns that change. For example, the output should look like -
ADDRESS_ID Changed Attribute Previous_Value Current_Value
300000009360612 Effective_start_Date 2000-03-27 2020-04-01
300000009360612 Effective_end_Date 2020-03-31 4712-12-31
300000009360612 ADDRESS_LINE_1 Box 13 Box 14
300000009360612 ADDRESS_LINE_2 ADAM SELLERS ST
I have tried -
select * from (
SELECT
address_id ,
'Effective_Start_Date' Changed_Attribute ,
cast(Effective_Start_Date as varchar(40)) current_value
, LAG(cast(effective_start_date as varchar(40)), 1, 0) OVER (partition by address_ID, effective_start_date ORDER BY last_update_date) Previous_Value
FROM fusion.per_addresses_f_
ORDER BY last_update_date DESC)
where current_value <> Previous_Value
The above query is not giving the right output. It is giving me the below output-
address_id Changed_Attribute current_value Previous_Value
300000009360612 Effective_start_Date 2020-04-01
i.e. i am not getting any value in previous_value.

select *
from (SELECT address_id,
'Effective_Start_Date' Changed_Attribute,
to_char(Effective_Start_Date, 'yyyy-mm-dd') current_value,
to_char(LAG(t.effective_start_date, 1) over(order by t.effective_start_date desc) , 'yyyy-mm-dd') Previous_Value
FROM AUDIT_DATA t
union all
SELECT address_id,
'Effective_end_Date' Changed_Attribute,
to_char(t.effective_end_date , 'yyyy-mm-dd') current_value,
to_char(LAG(t.effective_end_date, 1) over(order by t.effective_end_date desc) , 'yyyy-mm-dd') Previous_Value
FROM AUDIT_DATA t
union all
SELECT address_id,
'ADDRESS_LINE_1' Changed_Attribute,
t.address_line_1 current_value,
LAG(t.address_line_1, 1) over(order by t.address_line_1 desc) Previous_Value
FROM AUDIT_DATA t
union all
SELECT address_id,
'ADDRESS_LINE_2' Changed_Attribute,
Nvl(t.address_line_2, ' ') current_value,
LAG(t.address_line_2, 1) over(order by t.address_line_2) Previous_Value
FROM AUDIT_DATA t
) r
where Previous_Value <> current_value;

u can use ROW_NUMBER instead of LAG =
select * from (select address_id ,
'Effective_Start_Date' Changed_Attribute ,
cast(Effective_Start_Date as varchar(40)) current_value,
ROW_NUMBER() over(partition by address_ID order by last_update_date desc) as ROW_NO from fusion.per_addresses_f_ ) Q where q.ROW_NO=1 -- first of each partiotion = Previous_Value
....

You can generate the 6 rows for each ADDRESS and compare it as follows:
WITH CTE AS
(SELECT T.*,
ROW_NUMBER() OVER (PARTITION BY ADDRESS_ID ORDER BY EFFECTIVE_START_DATE DESC) AS RN
FROM YOUR_TABLE T)
SELECT * FROM
(SELECT TNEW.ADDRESS_ID,
DECODE(LVL,1,'Effective_start_Date',2,'Effective_end_Date',3,'ADDRESS_LINE_1',4,'ADDRESS_LINE_2',5,'TOWN') AS CHANGED_aTTRIBUTE,
DECODE(LVL,1,TOLD.Effective_start_Date,2,TOLD.Effective_end_Date,3,TOLD.ADDRESS_LINE_1,4,TOLD.ADDRESS_LINE_2,5,TOLD.TOWN) AS PREV_VALUE,
DECODE(LVL,1,TNEW.Effective_start_Date,2,TNEW.Effective_end_Date,3,TNEW.ADDRESS_LINE_1,4,TNEW.ADDRESS_LINE_2,5,TNEW.TOWN) AS CURRENT_VALUE
FROM CTE TNEW
JOIN CTE TOLD ON TNEW.ADDRESS_ID = TOLD.ADDRESS_ID AND TNEW.RN = 1 AND TOLD.RN=2
cross join (SELECT LEVEL AS LVL FROM DUAL CONNECT BY LEVEL <=5) L)
WHERE PREV_VALUE <> CURRENT_VALUE
OR (PREV_VALUE IS NULL AND CURRENT_VALUE IS NOT NULL)
OR (PREV_VALUE IS NOT NULL AND CURRENT_VALUE IS NULL)

Using the last_update_date to decide which record is the first one...
/*
ADDRESS_ID EFFECTIVE_START_DATE EFFECTIVE_END_DATE ADDRESS_LINE_1 ADDRESS_LINE_2 TOWN Person_id LAST_UPDATE_DATE
300000009360612 2020-04-01 4712-12-31 Box 14 ADAM SELLERS ST Mirror 157 2002-01-01
300000009360612 2000-03-27 2020-03-31 Box 13 Mirror 157 2002-02-02
*/
With a1 as (
select
cast('300000009360612' as varchar2(200)) as address_id
, cast('2020-04-01' as varchar2(200)) as EFFECTIVE_START_DATE
, cast('4712-12-31' as varchar2(200)) as EFFECTIVE_END_DATE
, cast('Box 14' as varchar2(200)) as ADDRESS_LINE_1
, cast('ADAM SELLERS ST' as varchar2(200)) as ADDRESS_LINE_2
, cast('Mirror' as varchar2(200)) as TOWN
, cast(157 as number(10)) as PERSON_ID
, to_date('2020-01-01', 'YYYY-MM-DD') as LAST_UPDATE_DATE
from dual
union all
select
'300000009360612', '2000-03-27', '2020-03-31', 'Box 13', NULL, 'Mirror', 157, to_date('2020-02-02', 'YYYY-MM-DD')
from dual
),
Q0 as (
select
address_id
, effective_start_date, effective_end_date
, address_line_1, address_line_2
, town, person_id
, effective_start_date as current_value
, LAG(effective_start_date, 1, 0) OVER (partition by person_id, address_id ORDER BY last_update_date) Previous_Value
from a1
),
Q1 as (
select
t1.address_id as address_id,
t1.effective_start_date as effective_start_date_t1, t2.effective_start_date as effective_start_date_t2,
t1.effective_end_date as effective_end_date_t1, t2.effective_end_date as effective_end_date_t2,
t1.address_line_1 as address_line_1_t1, t2.address_line_1 as address_line_1_t2,
t1.address_line_2 as address_line_2_t1, t2.address_line_2 as address_line_2_t2,
t1.town as town_t1, t2.town as town_t2,
to_char(t1.person_id) as person_id_t1, to_char(t2.person_id) as person_id_t2
, to_char(t1.last_update_date, 'YYYY-MM-DD') as last_update_date_t1, to_char(t2.last_update_date, 'YYYY-MM-DD') as last_update_date_t2
from a1 t1 inner join a1 t2 on (t1.person_id = t2.person_id and t1.address_id = t2.address_id and t1.LAST_UPDATE_DATE < t2.LAST_UPDATE_DATE)
where 1=1
order by t1.person_id, t1.address_id, t1.last_update_date
)
select
ADDRESS_ID
, COLX
, VAL1
, VAL2
--, case when nvl(VAL1, 'NA') != nvl(VAL2,'NA') THEN 1 ELSE 0 END CHANGED
from q1
unpivot (
(val1, val2)
for colx in (
(EFFECTIVE_START_DATE_T1, EFFECTIVE_START_DATE_T2) as 'EFFECTIVE_START_DATE'
, (EFFECTIVE_END_DATE_T1, EFFECTIVE_END_DATE_T2) as 'EFFECTIVE_END_DATE'
, (ADDRESS_LINE_1_T1, ADDRESS_LINE_1_T2) as 'ADDRESS_LINE_1'
, (ADDRESS_LINE_2_T1, ADDRESS_LINE_2_T2) as 'ADDRESS_LINE_2'
, (TOWN_T1, TOWN_T2) as 'TOWN'
, (PERSON_ID_T1, PERSON_ID_T2) as 'PERSON_ID'
, (LAST_UPDATE_DATE_T1, LAST_UPDATE_DATE_T2) as 'LAST_UPDATE_DATE'
)
)
where 1=1
and case when nvl(VAL1, 'NA') != nvl(VAL2,'NA') THEN 1 ELSE 0 END != 0
;
gives
ADDRESS_ID COLX VAL1 VAL2
300000009360612 EFFECTIVE_START_DATE 2020-04-01 2000-03-27
300000009360612 EFFECTIVE_END_DATE 4712-12-31 2020-03-31
300000009360612 ADDRESS_LINE_1 Box 14 Box 13
300000009360612 ADDRESS_LINE_2 ADAM SELLERS ST
300000009360612 LAST_UPDATE_DATE 2020-01-01 2020-02-02
NOTE: VAL1 = Previous Value and VAL2 = Current Value (based on LAST_UPDATE_DATE)

Related

SQL BigQuery. Check if user bought something previous month = old, if he didn't buy anything next month = churn

What we have:
user_id month
---- 2021-08
1414 2021-09
1414 2021-10
1414 2021-11
---- 2021-12
What we need:
user_id month
---- 2021-08
1414 2021-09 new user
1414 2021-10 old
1414 2021-11 churn
---- 2021-12
in the end, I'll aggregate all of the users with COUNT(new_user) and GROUP BY status...
However, I have a problem with this stage where I need to assign correct values to users within certain months
Something like this might work.
The first test CTE term is just to provide the test table data.
WITH test (user_id, month) AS (
SELECT 1414, '2021-09' UNION
SELECT 1414, '2021-10' UNION
SELECT 1414, '2021-11' UNION
SELECT null, '2021-08' UNION
SELECT null, '2021-12'
)
, xrows AS (
SELECT *
, LAG(month) OVER (PARTITION BY user_id ORDER BY month) AS lastval
, LEAD(month) OVER (PARTITION BY user_id ORDER BY month) AS nextval
FROM test
)
SELECT user_id, month
, CASE WHEN user_id IS NOT NULL THEN
CASE WHEN nextval IS NULL THEN 'churn'
WHEN lastval IS NULL THEN 'new user'
ELSE 'old'
END
END AS status
FROM xrows
ORDER BY month
;
-- or
WITH test (user_id, month) AS (
SELECT 1414, '2021-09' UNION
SELECT 1414, '2021-10' UNION
SELECT 1414, '2021-11' UNION
SELECT null, '2021-08' UNION
SELECT null, '2021-12'
)
, xrows AS (
SELECT *
, LAG(month) OVER w AS lastval
, LEAD(month) OVER w AS nextval
FROM test
WINDOW w AS (PARTITION BY user_id ORDER BY month)
)
SELECT user_id, month
, CASE WHEN user_id IS NOT NULL THEN
CASE WHEN nextval IS NULL THEN 'churn'
WHEN lastval IS NULL THEN 'new user'
ELSE 'old'
END
END AS status
FROM xrows
ORDER BY month
;
Result:
user_id
month
status
2021-08
1414
2021-09
new user
1414
2021-10
old
1414
2021-11
churn
2021-12

How to remove NULL values from two rows in a table

output I am getting is this.
2015-10-01 NULL
NULL NULL
NULL NULL
NULL 2015-10-05
2015-10-11 NULL
NULL 2015-10-13
2015-10-15 2015-10-16
2015-10-25 NULL
NULL NULL
NULL NULL
NULL NULL
NULL NULL
NULL 2015-10-31
I want this to be
2015-10-01 2015-10-05
2015-10-11 2015-10-13
2015-10-15 2015-10-16
2015-10-25 2015-10-31
My code:
select (case when (end_lag <> start_date) or end_lag is null then start_date end) as start_date,
(case when (start_lead <> end_date) or start_lead is null then end_date end) as end_date
from
(select lead(start_date) over(order by start_date) as start_lead, start_date, end_date, lag(end_date) over(order by end_date) as end_lag
from projects) t1;
original table has two attributes (start_date, end_date), I have created the lead column for start_date and lag column for end_date
From current results table would go with:
select start_date, end_date
from (select row_number() over(order by null) rn, start_date
from current_t
where start_date is not null) a
join (select row_number() over(order by null) rn, end_date
from current_t
where end_date is not null) b
on b.rn = a.rn;
(sql fiddle here)
You don't seem to have an ordering for your rows. So, you can just unpivot and pair them up:
select min(dte), nullif(max(dte), min(dte))
from (select x.dte, row_number() over (order by dte) as seqnum
from projects p cross join lateral
(select p.start_date as dte from dual union all
select p.end_date from dual
) x
) p
group by ceil(seqnum / 2)
Ignore two NULLs and take lead value from your original query. I guess it could be simplified, hard to know without DDL and sample data.
select *
from (
select start_date,
case when end_date is null then lead(end_date) over(order by coalesce(start_date, end_date)) else end_date end end_date
from (
select *
from (
-- your original query
select (case when (end_lag <> start_date) or end_lag is null then start_date end) as start_date,
(case when (start_lead <> end_date) or start_lead is null then end_date end) as end_date
from (
select lead(start_date) over(order by start_date) as start_lead, start_date, end_date,
lag(end_date) over(order by end_date) as end_lag
from projects) t1
---
) tbl
where not (start_date is null and end_date is null )
) t
) t
where start_date is not null
order by start_date;

query to retrieve changes in multiple columns in sql

I have the below table with the assignments details such as location, org, job, grade etc.
I want to build a query such that changes in the location, org are fetched for all system_person_type = 'EMP' only.
per_assignments
Person_id locat_id org_id job_id grade_id system_person_type START_DT END_DT
1 Toronto XYZ 1 GR1 EMP 01-JAN-2019 20-JAN-2019
1 US XYZ 1 GR1 EMP 21-JAN-2019 31-DEC-4712
2 Chicago ABC 2 GR1 EX-EMP 01-jul-2017 30-Nov-2017
2 Toronto XYZ 3 GR2 EMP 01-JAN-2019 03-JUL-2019
2 India GFH 3 GR2 EMP 04-JUL-2019 08-SEP-2019
2 India GFH 4 GR2 EMP 09-SEP-2019 31-DEC-4712
so in the above example the output should be :
person_id old_locat_id new_locat_id old_org_id new_org_id old_start_dt new_start_dat
1 Toronto US - - 01-jan-2019 21-jan-2019
2 Toronto India XYZ GFH 01-JAN-2019 04-JUL-2019
I created the below query But from the below query I am getting old_start_dt> new_start_dt and I am not getting all the changes required,
only 1 column change is retrieving. How can the below query be changed to accomadtae the above requirement ?
SELECT DISTINCT paam_change_loc.person_id ,
to_char(paam_change1.start_date,'YYYY-MM-DD') AS old_effective_start_dt ,
to_char(paam_change_loc.start_date,'YYYY-MM-DD') AS new_effective_start_dt ,
paam_change1.location_id AS old_loc_value ,
paam_change_loc.location_id AS new_loc_value
FROM per_assignments paam_change_loc,
per_assignments paam_change1
WHERE paam_change_loc.person_id =paam_change1.person_id
AND (
paam_change_loc.location_id IS NOT NULL
AND paam_change_loc.location_id <> paam_change1.location_id )
AND paam_change_loc.system_person_type = 'EMP'
AND paam_change1.system_person_type = 'EMP'
AND to_char(to_date(paam_change_loc.start_date),'DD-MM-YYYY') BETWEEN ('05-08-2019') AND '05-12-2019'
AND (
to_char(to_date(paam_change_loc.start_date)-1,'DD-MM-YYYY') BETWEEN ('05-08-2019') AND '05-12-2019' )
'05-08-2019' and '05-12-2019' is the transfer dates which will be passed to the query and the dates are to be compared in between these two dates
This query gives expected result:
select person_id, prev_start_dt, start_dt,
case loc_new when loc_old then ' - ' else loc_old end loc_old,
case loc_new when loc_old then ' - ' else loc_new end loc_new,
case org_new when org_old then ' - ' else org_old end org_old,
case org_new when org_old then ' - ' else org_new end org_new
from (
select person_id, locat_id loc_new, org_id org_new, start_dt,
lag(locat_id) over (partition by person_id order by start_dt) loc_old,
lag(org_id) over (partition by person_id order by start_dt) org_old,
lag(start_dt) over (partition by person_id order by start_dt) prev_start_dt,
case start_dt when 1 + lag(end_dt) over (partition by person_id order by start_dt)
then 1 end flag
from per_assignments)
where flag = 1 and (loc_new <> loc_old or org_new <> org_old)
dbfiddle
In the inner query apply filters for system_person_type and dates as needed. At first I used lag() three times and also to mark continuous rows, in column flag. Then only flagged rows are shown where location or organization changed.
I am not sure about data structure of your db. Considering the sample data as table, you can achieve the expected outptut using analytical function:
Select person_id,
Locat_id as old_locat_id,
New_locat_id,
org_id as old_org_id,
New_org_id,
Start_date as old_start_date,
New_start_date
From
(Select t.*,
Lead(org_id) over (partition by person_id order by start_date) as new_org_id,
Lead(start_date) over (partition by person_id order by start_date) as new_start_date,
Lead(locat_id) over (partition by person_id order by start_date) as new_locat_id,
From your_table t where system_person_type = 'EMP')
Where locat_id <> new_locat_id or org_id <> new_org_id;
Cheers!!

Insert the table data based on grouping of two columns

I have a oracle table with the following format,
For eg:
JLID Dcode SID TDT QTY
8295783 3119255 9842 3/5/2018 14
8269771 3119255 9842 3/6/2018 11
8302211 3119255 1126 3/1/2018 19
Here I have different SID for the same Dcode, now I need to get the SID with the maximum Qty. (i.e) for SID 9842 - (14+11)=25, for SID 1126 it is 19, then the results should be on SID 9842. So, our query should returns the following results
JLID Dcode START_DT END_DT SID
111 3119255 3/1/2018 3/31/2018 12:00 9842
Startdate and enddate should be calculated from TDT (i.e) start date is the first date of the month and the end date is the last date of the month
Can anyone please suggest me some ideas to do it.
It might be as simple as this:
SELECT Dcode, start_date, end_date, SID FROM (
SELECT Dcode, SID, TRUNC(start_date, 'MONTH') AS start_date
, LAST_DAY(end_date) AS end_date
, ROW_NUMBER() OVER ( PARTITION BY Dcode ORDER BY total_qty DESC ) AS rn
FROM (
SELECT Dcode, SID, MIN(TDT) AS start_date, MAX(TDT) AS end_date
, SUM(QTY) AS total_qty
FROM mytable
GROUP BY Dcode, SID
)
) WHERE rn = 1
In the inner most subquery I aggregation to get the range of dates and total quantity for particular values of Dcode and SID. Then I use an anaylitic (window) function to get the row for which total quantity is the greatest. (You would want to use RANK() in place of ROW_NUMBER() in the event you want to return more than one value of SID with the same quantity.)
Here's one option which doesn't contain JLID = 111 in the final result as I have no idea where you took it from.
SQL> with test (jlid, dcode, sid, tdt, qty) as
2 (select 8295783, 3119255, 9842, date '2018-03-05', 14 from dual union
3 select 8269771, 3119255, 9842, date '2018-08-22', 11 from dual union
4 select 8302211, 3119255, 1126, date '2018-03-01', 19 from dual union
5 --
6 select 1234567, 1112223, 1000, date '2018-06-16', 88 from dual
7 )
8 select dcode,
9 min (trunc (tdt, 'mm')) start_dt, --> MIN
10 max (last_day (tdt)) end_dt, --> MAX
11 sid
12 from (select dcode,
13 sid,
14 tdt,
15 sqty,
16 rank () over (partition by dcode order by sqty desc) rnk
17 from (select dcode,
18 sid,
19 tdt,
20 sum (qty) over (partition by dcode, sid) sqty
21 from test))
22 where rnk = 1
23 group by dcode, sid; --> GROUP BY
DCODE START_DT END_DT SID
---------- ---------------- ---------------- ----------
1112223 01.06.2018 00:00 30.06.2018 00:00 1000
3119255 01.03.2018 00:00 31.08.2018 00:00 9842
SQL>

Oracle query to get how many days a record has certain status before today

I need query to get how many days last time a meter has been online.
For example :
METER PDATE STATUS
ABC 1-Jan off
ABC 2-Jan on
ABC 3-Jan on
ABC 4-Jan on
ABC 5-Jan off
ABC 6-Jan off
ABC 7-Jan on
ABC 8-Jan on
ABC 9-Jan off
If today is Jan 8th than the query will return : 3 (Jan 2-4).
If today is Jan 9th than the query will return : 2 (Jan 7-8).
My query below is working OK, but it takes 40-50 seconds if applied to the real table which has 5 millions records.
Please let me know if there are faster ways to get such data.
with last_off as
(
select meter,pdate lastoff from
(
select meter, pdate,
row_number() over (partition by meter order by pdate desc) rnum
from mytable
where status = 'off'
)
where rnum=1
),
last_on as
(
select meter, laston from
(
select a.meter, a.pdate laston, b.lastoff,
row_number() over (partition by a.meter order by a.pdate desc) rnum
from mytable a, last_off b
where status = 'on'
and a.meter=b.meter(+) and a.pdate < b.lastoff
)
where rnum=1
),
days_on as
(
select meter, laston-pdate dayson from
(
select a.meter, a.pdate, b.laston,
row_number() over (partition by a.meter order by a.pdate desc) rnum
from mytable a, last_on b
where status = 'off'
and a.meter=b.meter(+) and a.pdate < b.laston
)
where rnum=1
)
select meter, dayson
from days_on
with t as (
select meter, pdate, status,
case when lag(status) over (partition by meter order by pdate)
< status then 1 end chg1,
case when lead(status) over (partition by meter order by pdate)
< status then 1 end chg2
from mytable),
d2 as (
select meter, max(pdate) do2
from t where chg2 = 1 and pdate < date '2015-01-09' group by meter),
d1 as (
select meter, max(pdate) do1 from t join d2 using (meter)
where chg1 = 1 and pdate < d2.do2 group by meter)
select meter, do2-do1+1 days_on from d1 join d2 using (meter)
SQLFiddle demo
Change value in line containing date '2015-01-09' to whatever value you want, probably trunc(sysdate). Also change last line to:
select meter, count(1) cnt from t join d1 using (meter) join d2 using (meter)
where pdate between do1 and do2 group by (meter)
if you want to count rows from main table instead of simple subtracting days.
This would get the list of meters that have been on, and how many days they've been on.
(caveat: I don't have an Oracle instance to try this on as I'm writing it)
select maxon.METER,
(maxon.maxdate - maxoff.maxdate) as dayson
from
(select METER,
Max(PDATE) maxdate
from MY_TABLE
where PSTATUS = 'on'
group by meter) as maxon,
(select METER,
Max(PDATE) maxdate
from MY_TABLE
where PSTATUS = 'off'
group by meter) as maxoff
where maxon.meter = maxoff.meter
and maxon.maxdate > maxoff.maxdate;
You could union a second query to get the meters that have been off, or just be more clever in how you interpret the subtraction result (i.e., do a CASE statement such that if the result is negative, it's off and if positive, it's on)
http://www.techonthenet.com/oracle/functions/case.php
Data Setup:
CREATE TABLE my_table
(METER varchar2(3), PDATE date, STATUS varchar2(3))
;
INSERT ALL
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '01-Jan-2001', 'off')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '02-Jan-2001', 'on')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '03-Jan-2001', 'on')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '04-Jan-2001', 'on')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '05-Jan-2001', 'off')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '06-Jan-2001', 'off')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '07-Jan-2001', 'on')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '08-Jan-2001', 'on')
INTO my_table (METER, PDATE, STATUS)
VALUES ('ABC', '09-Jan-2001', 'off')
SELECT * FROM dual
;
You can make use of Analytical function called lag
Query:
SELECT meter,
pdate,
pdate - Min (pdate)
over(
ORDER BY grp DESC) + 1 AS daysoff
FROM (SELECT meter,
pdate,
status,
Max(grp)
over(
ORDER BY pdate) grp
FROM (SELECT meter,
pdate,
status,
CASE
WHEN Lag(status)
over (
ORDER BY pdate) != ( status ) THEN
Row_number()
over (
ORDER BY pdate)
WHEN Row_number()
over (
ORDER BY pdate) = 1 THEN 1
END grp
FROM my_table))
WHERE status = 'on'
ORDER BY pdate ASC;
Results:
METER PDATE DAYSOFF
ABC January, 02 2001 00:00:00 1
ABC January, 03 2001 00:00:00 2
ABC January, 04 2001 00:00:00 3
ABC January, 07 2001 00:00:00 1
ABC January, 08 2001 00:00:00 2