Last changed Data with T as status in Oracle sql - sql

My Data is given below
In the below sample latest record has T and last occurrence of T was updated on 3-Apr-17 so that row needs to be displayed
EMP EFFDT STATUS
11367 15-Apr-15 A
11367 14-Jun-15 A
11367 10-Aug-15 T
11367 2-Apr-17 A
11367 3-Apr-17 T *
11367 10-Apr-17 T
In the below sample latest record has T and last occurrence of T was updated on 23-Feb-18 so that row needs to be displayed
EMP EFFDT STATUS
20612 4-Sep-16 A
20612 23-Feb-18 T *
20612 20-Jul-18 T
In the below sample latest record has T and that is the only occurrence so display it
EMP EFFDT STATUS
20644 12-Jul-15 A
20644 8-Aug-16 A
20644 6-Oct-16 T*
In the below sample latest record does not has T so no need to display
EMP EFFDT STATUS
21155 18-May-17 T
21155 21-Jun-17 A
21155 13-Mar-18 T
21155 15-Aug-18 A
My Desired Output should be (* marked records)
EMP EFFDT STATUS
11367 3-Apr-17 T
20612 23-Feb-18 T
20644 6-Oct-16 T

This is an island and gap problem.
In the cte you try to found out what island have T as last update (t=0)
SQL DEMO
WITH cte as (
SELECT "EMP",
"EFFDT",
SUM(CASE WHEN "STATUS" <> 'T'
THEN 1
ELSE 0
END) OVER (partition by "EMP" ORDER BY "EFFDT" DESC) as t
FROM Table1
)
SELECT "EMP", MIN("EFFDT") as "EFFDT", MAX('T') as "STATUS"
FROM cte
WHERE t = 0
GROUP BY "EMP"
OUTPUT
| EMP | EFFDT | STATUS |
|-------|-----------------------|--------|
| 11367 | 2017-04-03 00:00:00.0 | T |
| 20612 | 2018-02-23 00:00:00.0 | T |
| 20644 | 2016-10-06 00:00:00.0 | T |
For debug you can try
SELECT *
FROM cte
to see how t values are created

WITH cte1
AS (
SELECT A.*
,lag(STATUS, 1, 0) OVER (
PARTITION BY EMP ORDER BY EFFDT
) AS PRIOR_STATUS
FROM Table1 A
)
SELECT EMP
,STATUS
,MAX(EFFDT) AS EFFDT
FROM cte1 A
WHERE A.STATUS = 'T'
AND A.PRIOR_STATUS <> 'T'
GROUP BY EMP
,STATUS
SQL Fiddle here: http://sqlfiddle.com/#!4/458733/18

alter session set nls_date_format = 'dd-Mon-rr';
Solution (including simulated data in with clause):
with
simulated_data (EMP, EFFDT, STATUS) as (
select 11367, to_date('15-Apr-15'), 'A' from dual union all
select 11367, to_date('14-Jun-15'), 'A' from dual union all
select 11367, to_date('10-Aug-15'), 'T' from dual union all
select 11367, to_date( '2-Apr-17'), 'A' from dual union all
select 11367, to_date( '3-Apr-17'), 'T' from dual union all
select 11367, to_date('10-Apr-17'), 'T' from dual union all
select 20612, to_date( '4-Sep-16'), 'A' from dual union all
select 20612, to_date('23-Feb-18'), 'T' from dual union all
select 20612, to_date('20-Jul-18'), 'T' from dual union all
select 20644, to_date('12-Jul-15'), 'A' from dual union all
select 20644, to_date( '8-Aug-16'), 'A' from dual union all
select 20644, to_date( '6-Oct-16'), 'T' from dual union all
select 21155, to_date('18-May-17'), 'T' from dual union all
select 21155, to_date('21-Jun-17'), 'A' from dual union all
select 21155, to_date('13-Mar-18'), 'T' from dual union all
select 21155, to_date('15-Aug-18'), 'A' from dual
)
-- End of simulated data (for testing only).
-- SQL query (solution) begins BELOW THIS LINE.
select emp, min(effdt) as eff_dt, 'T' as status
from (
select emp, effdt, status,
row_number() over (partition by emp, status
order by effdt desc) as rn,
min(status) keep (dense_rank last order by effdt)
over (partition by emp) as last_status
from simulated_data
)
where last_status = 'T' and status = 'T' and rn <= 2
group by emp
;
Output:
EMP EFF_DT STATUS
---------- --------- ------
11367 03-Apr-17 T
20612 23-Feb-18 T
20644 06-Oct-16 T
Explanation:
In the subquery, we add two columns to the input data. Column RN gives a rank within each partition by EMPNO and STATUS, in descending order by EFFDT. LAST_STATUS used the analytic version of the LAST() function to assign either T or A as the last status for each EMP (and it attaches this value to EVERY row for the EMP, regardless of each row's own STATUS).
In the outer query, we are only interested to retain the EMP where the last status was T. For those rows, we only want to retain the rows where the actual status of the row is in fact T (we know this will always include the last row for that EMP, by the way, and it will have RN = 1). Moreover, we are only interested in those rows where RN is 1 or possibly 2 (if there are at least two rows with status T for that EMP). Of these either one or two rows with status T for a given EMP, we want to get the EARLIEST date. That will be the ONLY date if there is no row with RN = 2 for that partition; otherwise, it will be the date from the earlier row, with RN = 2.
In the outer SELECT we select the EMP, the earliest date, and the status we already know, it is T (so we don't need any work for this - actually it is not clear why the third column is even needed, since it is known beforehand it will be T in all rows).

Assuming that A and T are the only statuses, this should work.
WITH cte1
AS (
SELECT A.EMP, A.EFFDT, A.STATUS
,min(STATUS) OVER (
PARTITION BY EMP ORDER BY EFFDT RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) AS MIN_STATUS
FROM Table1 A
)
SELECT
cte1.EMP
,MIN(cte1.EFFDT) AS EFFDT
,MIN(cte1.STATUS) as STATUS
FROM cte1
WHERE cte1.MIN_STATUS = 'T'
GROUP BY EMP
EDIT: well, if you have another statues, let's make it more robust. Actually, it's almost the same as juan-carlos-oropeza proposed, but he missed "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" part.
Ooops, it IS the same solution: juan-carlos-oropeza used order by DESC istead of unbounded following.
with emp_status_log (EMP, EFFDT, STATUS) as
(
select 11367, to_date('15-Apr-15', 'dd-Mon-yy'), 'A' from dual union all
select 11367, to_date('14-Jun-15', 'dd-Mon-yy'), 'A' from dual union all
select 11367, to_date('10-Aug-15', 'dd-Mon-yy'), 'T' from dual union all
select 11367, to_date( '2-Apr-17', 'dd-Mon-yy'), 'A' from dual union all
select 11367, to_date( '3-Apr-17', 'dd-Mon-yy'), 'T' from dual union all
select 11367, to_date('10-Apr-17', 'dd-Mon-yy'), 'T' from dual union all
select 20612, to_date( '4-Sep-16', 'dd-Mon-yy'), 'A' from dual union all
select 20612, to_date('23-Feb-18', 'dd-Mon-yy'), 'T' from dual union all
select 20612, to_date('20-Jul-18', 'dd-Mon-yy'), 'T' from dual union all
select 20644, to_date('12-Jul-15', 'dd-Mon-yy'), 'A' from dual union all
select 20644, to_date( '8-Aug-16', 'dd-Mon-yy'), 'A' from dual union all
select 20644, to_date( '6-Oct-16', 'dd-Mon-yy'), 'T' from dual union all
select 21155, to_date('18-May-17', 'dd-Mon-yy'), 'T' from dual union all
select 21155, to_date('21-Jun-17', 'dd-Mon-yy'), 'A' from dual union all
select 21155, to_date('13-Mar-18', 'dd-Mon-yy'), 'T' from dual union all
select 21155, to_date('15-Aug-18', 'dd-Mon-yy'), 'A' from dual
)
,
-- End of simulated data (for testing only).
/* SQL query (solution) begins BELOW THIS LINE.
with--*/
cte1 as
(
select sl.*
,sum(decode(sl.STATUS, 'T', 0, 1)) OVER (
PARTITION BY sl.EMP ORDER BY sl.EFFDT RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) AS non_t_count
from emp_status_log sl
)
select
cte1.emp
, min(cte1.effdt) as effdt
, min(cte1.status) as status
from cte1
where cte1.non_t_count = 0
group by cte1.emp

Related

how to find the maximum occurence of a string in Oracle SQL developer

i have 2 columns in a table. Data looks like this
Folio_no | Flag
1145 R
201 S
1145 FR
300 E
1145 R
201 E
201 S
Expected Output:
Folio_No | Flag
1145 R
201 S
300 E
The output should give the folio_no along with the flag which occured maximum number of times for that particular folio number.
i tried doing the below but it throws an error
select folio_no, max(count(flag)) from table group by folio_no;
We can use an aggregation:
WITH cte AS (
SELECT Folio_No, Flag, COUNT(*) AS cnt
FROM yourTable
GROUP BY Folio_No, Flag
),
cte2 AS (
SELECT t.*, RANK() OVER (PARTITION BY Folio_No ORDER BY cnt DESC, Flag) rnk
FROM cte t
)
SELECT Folio_No, Flag
FROM cte2
WHERE rnk = 1;
Note that I assume should two flags within a given folio number be tied for the max frequency, that you want to report the earlier flag.
Here is a working demo.
If you want the flag(s) that have the maximum occurrence for each folio then you can use:
SELECT Folio_No, Flag
FROM (
SELECT Folio_No,
Flag,
RANK() OVER (PARTITION BY Folio_No ORDER BY COUNT(*) DESC) AS rnk
FROM table_name
GROUP BY Folio_No, Flag
)
WHERE rnk = 1;
Which, for the sample data:
CREATE TABLE table_name (folio_no, flag) AS
SELECT 1145, 'R' FROM DUAL UNION ALL
SELECT 201, 'S' FROM DUAL UNION ALL
SELECT 1145, 'FR' FROM DUAL UNION ALL
SELECT 300, 'E' FROM DUAL UNION ALL
SELECT 1145, 'R' FROM DUAL UNION ALL
SELECT 201, 'E' FROM DUAL UNION ALL
SELECT 201, 'S' FROM DUAL UNION ALL
SELECT 201, 'S' FROM DUAL UNION ALL
SELECT 1, 'A' FROM DUAL UNION ALL
SELECT 1, 'A' FROM DUAL UNION ALL
SELECT 1, 'B' FROM DUAL UNION ALL
SELECT 1, 'B' FROM DUAL UNION ALL
SELECT 1, 'C' FROM DUAL UNION ALL
SELECT 1, 'D' FROM DUAL;
Outputs:
FOLIO_NO
FLAG
1
A
1
B
201
S
300
E
1145
R
If you want only a single flag with the maximum occurrence for each folio, and if there are ties then the first folio alphabetically in each folio, then:
SELECT Folio_No, Flag
FROM (
SELECT Folio_No,
Flag,
ROW_NUMBER() OVER (PARTITION BY Folio_No ORDER BY COUNT(*) DESC, flag) AS rn
FROM table_name
GROUP BY Folio_No, Flag
)
WHERE rn = 1;
Which, for the sample data outputs:
FOLIO_NO
FLAG
1
A
201
S
300
E
1145
R
db<>fiddle here

SQL SELECT multiple max values for a date

I am trying to get the latest record from a table by filtering a column with max, but it looks like I have multiple records for the same max date. I have added an ORDER BY at the end of the query and looks like I can actually retrieve the latest value since it has a correct order, but i don't know how.
Below you will find a descriptive image:
Also please find below the query that I am using:
SELECT *
FROM item_forecast_detail
WHERE item_id = 177010 AND
forecast_dt = (SELECT MAX(forecast_dt)
FROM item_forecast_detail
WHERE item_id = 177010)
ORDER BY forecast_dt DESC
One option is to apply another condition, e.g. fetch the latest starting_hour:
SQL> with item_forecast_Detail (item_id, forecast_dt, starting_hour) as
2 (select 177010, date '2019-07-07', 21 from dual union all
3 select 177010, date '2019-07-07', 18 from dual union all
4 select 177010, date '2019-07-07', 15 from dual union all
5 select 177010, date '2019-07-07', 12 from dual union all
6 --
7 select 123456, date '2019-02-17', 09 from dual
8 )
9 select *
10 from item_forecast_Detail i
11 where i.item_id = 177010
12 and i.forecast_dt = (select max(i1.forecast_dt)
13 from item_forecast_detail i1
14 where i1.item_id = i.item_id
15 )
16 and i.starting_hour = (select max(i2.starting_hour)
17 from item_forecast_detail i2
18 where i2.item_id = i.item_id
19 );
ITEM_ID FORECAST_D STARTING_HOUR
---------- ---------- -------------
177010 07.07.2019 21
SQL>
Another one is to sort them by using analytical function and apply it to the final query:
SQL> with item_forecast_Detail (item_id, forecast_dt, starting_hour) as
2 (select 177010, date '2019-07-07', 21 from dual union all
3 select 177010, date '2019-07-07', 18 from dual union all
4 select 177010, date '2019-07-07', 15 from dual union all
5 select 177010, date '2019-07-07', 12 from dual union all
6 --
7 select 123456, date '2019-02-17', 09 from dual
8 ),
9 sort as
10 (select i.*,
11 row_number() over (partition by item_id order by forecast_dt, starting_hour desc) rn
12 from item_forecast_Detail i
13 )
14 select *
15 from sort s
16 where s.item_id = 177010
17 and s.rn = 1;
ITEM_ID FORECAST_D STARTING_HOUR RN
---------- ---------- ------------- ----------
177010 07.07.2019 21 1
SQL>
Use row_number() window analytic function to order as desired, and max(forecast_dt) over (order by forecast_dt desc) to detect the latest date
SELECT *
FROM
(
SELECT d.*,
row_number() over (order by ending_hour desc) as rn,
max(forecast_dt) over (order by forecast_dt desc) as mx
FROM item_forecast_detail d
WHERE item_id=177010
)
WHERE mx = forecast_dt
ORDER BY rn
You could try this with MySQL, SQLServer:
SELECT * FROM TABLE ORDER BY FORECAST_DT DESC LIMIT 1
SELECT TOP 1 * FROM Table ORDER BY FORECAST_DT DESC
You can use ROWNUM.
-- FETCH MAX FOR SINGLE item_id
WITH item_forecast_detail(item_id, forecast_dt, SOME_OTHER_COLS) AS (
SELECT 177010, DATE '2019-07-01','ANY VALUE - 1' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-01','ANY VALUE - 2' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-02','ANY VALUE - 4' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-02','ANY VALUE - 5' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-02','ANY VALUE - 6' FROM DUAL UNION ALL
SELECT 177011, DATE '2019-07-01','ANY VALUE - 1' FROM DUAL UNION ALL
SELECT 177011, DATE '2019-07-01','ANY VALUE - 1' FROM DUAL UNION ALL
SELECT 177011, DATE '2019-06-30','ANY VALUE - 1' FROM DUAL
)
SELECT
ITEM_ID,
FORECAST_DT,
SOME_OTHER_COLS
FROM
(
SELECT
ITEM_ID,
FORECAST_DT,
SOME_OTHER_COLS,
ROW_NUMBER() OVER(
PARTITION BY ITEM_ID
ORDER BY
FORECAST_DT DESC
) AS RN
FROM
ITEM_FORECAST_DETAIL
WHERE
ITEM_ID = 177010
)
WHERE
RN = 1
OUTPUT
-- FETCH MAX FOR MULTIPLE item_id
WITH item_forecast_detail(item_id, forecast_dt, SOME_OTHER_COLS) AS (
SELECT 177010, DATE '2019-07-01','ANY VALUE - 1' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-01','ANY VALUE - 2' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-02','ANY VALUE - 4' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-02','ANY VALUE - 5' FROM DUAL UNION ALL
SELECT 177010, DATE '2019-07-02','ANY VALUE - 6' FROM DUAL UNION ALL
SELECT 177011, DATE '2019-07-01','ANY VALUE - 1' FROM DUAL UNION ALL
SELECT 177011, DATE '2019-07-01','ANY VALUE - 1' FROM DUAL UNION ALL
SELECT 177011, DATE '2019-06-30','ANY VALUE - 1' FROM DUAL
)
SELECT
ITEM_ID,
FORECAST_DT,
SOME_OTHER_COLS
FROM
(
SELECT
ITEM_ID,
FORECAST_DT,
SOME_OTHER_COLS,
ROW_NUMBER() OVER(
PARTITION BY ITEM_ID
ORDER BY
FORECAST_DT DESC
) AS RN
FROM
ITEM_FORECAST_DETAIL
--WHERE item_id = 177010
)
WHERE
RN = 1
OUTPUT
DB<>FIDDLE DEMO
Cheers!!

SQL Query returning Null for Value present

I have a sql table which has columns like ITEM_ID, FROM_QUANTITY, TO_QUANTITY, LIST_PRICE. Now every item should have a FROM_QUANTITY from 1 to 12 and a null in FROM_QUANTITY.
Now in order to check which QUANTITY is missing from ITEM in table , I have written a SQL query which will print None if the FROM_QUANTITY is not there.
The query is working as expected. Just that each item also has a null value in quantity and has some price associated with it. But my query is Showing null as list price for it.
Here is my query
with pl as (
select DISTINCT ITEM_ID, FROM_QUANTITY,TO_QUANTITY, coalesce(to_char(list_price), 'NONE') as list_price
from PRICELIST_LINE
where item_id IN ('XYZ') and
PRICELIST_HDR_KEY in (select Pricelist_Hdr_Key
from PRICELIST_HDR
where PRICELIST_NAME IN ('ABC') and
SELLER_ORGANIZATION_CODE IN ('100')
) and SYSDATE < END_DATE_ACTIVE
)
select i.item_id, u.from_quantity, pl.to_quantity,pl.list_price
from (select distinct item_id from pl) i cross join
(select '1' as from_quantity from dual union all
select '2' as from_quantity from dual union all
select '3' as from_quantity from dual union all
select '4' as from_quantity from dual union all
select '5' as from_quantity from dual union all
select '6' as from_quantity from dual union all
select '7' as from_quantity from dual union all
select '8' as from_quantity from dual union all
select '9' as from_quantity from dual union all
select '10' as from_quantity from dual union all
select '11' as from_quantity from dual union all
select '12' as from_quantity from dual union all
select '' as from_quantity from dual
) u left join
pl
on pl.item_id = i.item_id and pl.from_quantity = u.from_quantity;
Now the Output for this is like:
ITEM_ID FROM_QUANTITY TO_QUANTITY LIST_PRICE
------- ------------- ----------- ----------
ABC 1 2 100
ABC
Expected :
ITEM_ID FROM_QUANTITY TO_QUANTITY LIST_PRICE
------- ------------- ----------- ----------
ABC 1 2 100
ABC 200
Using a partition outer join in Oracle should help, e.g.:
WITH pl AS (SELECT DISTINCT item_id,
from_quantity,
to_quantity,
coalesce(to_char(list_price), 'NONE') AS list_price
FROM pricelist_line
WHERE item_id IN ('XYZ')
AND pricelist_hdr_key IN (SELECT pricelist_hdr_key
FROM pricelist_hdr
WHERE pricelist_name IN ('ABC')
AND seller_organization_code IN ('100'))
AND SYSDATE < end_date_active),
qtys AS (SELECT LEVEL from_quantity
FROM dual
CONNECT BY LEVEL <= 12
UNION ALL
SELECT NULL from_quantity
FROM dual)
SELECT pl.item_id,
q.from_quantity,
pl.to_quantity,
pl.list_price
FROM qtys q
LEFT OUTER JOIN PL PARTITION BY (pl.item_id) ON pl.from_quantity = q.from_quantity
OR (pl.from_quantity IS NULL AND q.from_quantity IS NULL);
N.B. untested.

converting comma separated value to multiple rows

I have a table like this:
ID NAME Dept_ID
1 a 2,3
2 b
3 c 1,2
Department is another table having dept_id and dept_name as columns. i want result like,
ID Name Dept_ID
1 a 2
1 a 3
2 b
3 c 1
3 c 2
any help please?
You can do it as:
--Dataset Preparation
with tab(ID, NAME,Dept_ID) as (Select 1, 'a', '2,3' from dual
UNION ALL
Select 2, 'b','' from dual
UNION ALL
Select 3, 'c' , '1,2' from dual)
--Actual Query
select distinct ID, NAME, regexp_substr(DEPT_ID,'[^,]+', 1, level)
from tab
connect by regexp_substr(DEPT_ID,'[^,]+', 1, level) is not null
order by 1;
Edit:
based on which column i need to join? in one table i have comma
separated ids and in other table i have just ids
with tab(ID, NAME,Dept_ID) as (Select 1, 'a', '2,3' from dual
UNION ALL
Select 2, 'b','' from dual
UNION ALL
Select 3, 'c' , '1,2' from dual) ,
--Table Dept
tbl_dept (dep_id,depname) as ( Select 1,'depa' from dual
UNION ALL
Select 2,'depb' from dual
UNION ALL
Select 3,'depc' from dual
) ,
--Seperating col values for join. Start your query from here using with clause since you already have the two tables.
tab_1 as (select distinct ID, NAME, regexp_substr(DEPT_ID,'[^,]+', 1, level) col3
from tab
connect by regexp_substr(DEPT_ID,'[^,]+', 1, level) is not null
order by 1)
--Joining table.
Select t.id,t.name,t.col3,dt.depname
from tab_1 t
left outer join tbl_dept dt
on t.col3 = dt.dep_id
order by 1
with tmp_tbl as(
select
1 ID,
'a' NAME,
'2,3' DEPT_ID
from dual
union all
select
2 ID,
'b' NAME,
'' DEPT_ID
from dual
union all
select
3 ID,
'c' NAME,
'1,2' DEPT_ID
from dual)
select
tmp_out.ID,
tmp_out.NAME,
trim(tmp_out.DEPT_ID_splited)
from(
select
tmp.ID,
tmp.NAME,
regexp_substr(tmp.DEPT_ID,'[^,]+', 1, level) DEPT_ID_splited
from
tmp_tbl tmp
connect by
regexp_substr(tmp.DEPT_ID,'[^,]+', 1, level) is not null) tmp_out
group by
tmp_out.ID,
tmp_out.NAME,
tmp_out.DEPT_ID_splited
order by
tmp_out.ID,
tmp_out.DEPT_ID_splited

How to count consecutive duplicates in a table?

I have below question:
Want to find the consecutive duplicates
SLNO NAME PG
1 A1 NO
2 A2 YES
3 A3 NO
4 A4 YES
6 A5 YES
7 A6 YES
8 A7 YES
9 A8 YES
10 A9 YES
11 A10 NO
12 A11 YES
13 A12 NO
14 A14 NO
We will consider the value of PG column and I need the output as 6 which is the count of maximum consecutive duplicates.
It can be done with Tabibitosan method. Run this, to understand it:
with a as(
select 1 slno, 'A' pg from dual union all
select 2 slno, 'A' pg from dual union all
select 3 slno, 'B' pg from dual union all
select 4 slno, 'A' pg from dual union all
select 5 slno, 'A' pg from dual union all
select 6 slno, 'A' pg from dual
)
select slno, pg, newgrp, sum(newgrp) over (order by slno) grp
from(
select slno,
pg,
case when pg <> nvl(lag(pg) over (order by slno),1) then 1 else 0 end newgrp
from a
);
Newgrp means a new group is found.
Result:
SLNO PG NEWGRP GRP
1 A 1 1
2 A 0 1
3 B 1 2
4 A 1 3
5 A 0 3
6 A 0 3
Now, just use a group by with count, to find the group with maximum number of occurrences:
with a as(
select 1 slno, 'A' pg from dual union all
select 2 slno, 'A' pg from dual union all
select 3 slno, 'B' pg from dual union all
select 4 slno, 'A' pg from dual union all
select 5 slno, 'A' pg from dual union all
select 6 slno, 'A' pg from dual
),
b as(
select slno, pg, newgrp, sum(newgrp) over (order by slno) grp
from(
select slno, pg, case when pg <> nvl(lag(pg) over (order by slno),1) then 1 else 0 end newgrp
from a
)
)
select max(cnt)
from (
select grp, count(*) cnt
from b
group by grp
);
with test as (
select 1 slno,'A1' name ,'NO' pg from dual union all
select 2,'A2','YES' from dual union all
select 3,'A3','NO' from dual union all
select 4,'A4','YES' from dual union all
select 6,'A5','YES' from dual union all
select 7,'A6','YES' from dual union all
select 8,'A7','YES' from dual union all
select 9,'A8','YES' from dual union all
select 10,'A9','YES' from dual union all
select 11,'A10','NO' from dual union all
select 12,'A11','YES' from dual union all
select 13,'A12','NO' from dual union all
select 14,'A14','NO' from dual),
consecutive as (select row_number() over(order by slno) rr, x.*
from test x)
select x.* from Consecutive x
left join Consecutive y on x.rr = y.rr+1 and x.pg = y.pg
where y.rr is not null
order by x.slno
And you can control output with condition in where.
where y.rr is not null query returns duplicates
where y.rr is null query returns "distinct" values.
Just for completeness, here's the actual Tabibitosan method:
with sample_data as (select 1 slno, 'A1' name, 'NO' pg from dual union all
select 2 slno, 'A2' name, 'YES' pg from dual union all
select 3 slno, 'A3' name, 'NO' pg from dual union all
select 4 slno, 'A4' name, 'YES' pg from dual union all
select 6 slno, 'A5' name, 'YES' pg from dual union all
select 7 slno, 'A6' name, 'YES' pg from dual union all
select 8 slno, 'A7' name, 'YES' pg from dual union all
select 9 slno, 'A8' name, 'YES' pg from dual union all
select 10 slno, 'A9' name, 'YES' pg from dual union all
select 11 slno, 'A10' name, 'NO' pg from dual union all
select 12 slno, 'A11' name, 'YES' pg from dual union all
select 13 slno, 'A12' name, 'NO' pg from dual union all
select 14 slno, 'A14' name, 'NO' pg from dual)
-- end of mimicking a table called "sample_data" containing your data; see SQL below:
select max(cnt) max_pg_in_queue
from (select count(*) cnt
from (select slno,
name,
pg,
row_number() over (order by slno)
- row_number() over (partition by pg
order by slno) grp
from sample_data)
where pg = 'YES'
group by grp);
MAX_PG_IN_QUEUE
---------------
6
SELECT MAX(consecutives) -- Block 1
FROM (
SELECT t1.pg, t1.slno, COUNT(*) AS consecutives -- Block 2
FROM test t1 INNER JOIN test t2 ON t1.pg = t2.pg
WHERE t1.slno <= t2.slno
AND NOT EXISTS (
SELECT * -- Block 3
FROM test t3
WHERE t3.slno > t1.slno
AND t3.slno < t2.slno
AND t3.pg != t1.pg
)
GROUP BY t1.pg, t1.slno
);
The query calculates the result in following way:
Extract all couples of records that don't have a record with different value of PG in between (blocks 2 and 3)
Group them by PG value and starting SLNO value -> this counts the consecutive values for any [PG, (starting) SLNO] couple (block 2);
Extract Maximum value from query 2 (block 1)
Note that the query may be simplified if the slno field in table contains consecutive values, but this seems not your case (in your example record with SLNO = 5 is missing)
Only requiring a single aggregation query and no joins (the rest of the calculation can be done with ROW_NUMBER, LAG and LAST_VALUE):
SELECT MAX( num_before_in_queue ) AS max_sequential_in_queue
FROM (
SELECT rn - LAST_VALUE( has_changed ) IGNORE NULL OVER ( ORDER BY ROWNUM ) + 1
AS num_before_in_queue
FROM (
SELECT pg,
ROW_NUMBER() OVER ( ORDER BY slno ) AS rn,
CASE pg WHEN LAG( pg ) OVER ( ORDER BY slno )
THEN NULL
ELSE ROW_NUMBER() OVER ( ORDER BY sl_no )
END AS change
FROM table_name
)
WHERE pg = 'Y'
);
Try to use row_number()
select
SLNO,
Name,
PG,
row_number() over (partition by PG order by PG) as 'Consecutive'
from
<table>
order by
SLNO,
NAME,
PG
This is should work with minor tweaking.
--EDIT--
Sorry, partiton by PG.
The partitioning tells the row_number when to start a new sequence.