calculate percentage of two select counts - oracle

I have a query like
select count(1) from table_a where state=1;
it gives 20
select count(1) from table_a where state in (1,2);
it gives 25
I would like to have a query to extract percentage 80% (will be 20*100/25).
Is possible to have these in only one query?

I think without testing that the following SQL command can do that
SELECT SUM(CASE WHEN STATE = 1 THEN 1 ELSE 0 END)
/SUM(CASE WHEN STATE IN (1,2) THEN 1 ELSE 0 END)
as PERCENTAGE
FROM TABLE_A
or the following
SELECT S1 / (S1 + S2) as S1_PERCENTAGE
FROM
(
SELECT SUM(CASE WHEN STATE = 1 THEN 1 ELSE 0 END) as S1
,SUM(CASE WHEN STATE = 2 THEN 1 ELSE 0 END) as S2
FROM TABLE_A
)
or the following
SELECT S1 / T as S1_PERCENTAGE
FROM
(
SELECT SUM(CASE WHEN STATE = 1 THEN 1 ELSE 0 END) as S1
,SUM(CASE WHEN STATE IN (1,2) THEN 1 ELSE 0 END) as T
FROM TABLE_A
)
you have the choice for performance or readability !

Just as a slight variation on #schlebe's first query, you can continue to use count() by making that conditional:
select count(case when state = 1 then state end)
/ count(case when state in (1, 2) then state end) as result
from table_a
or multiplying by 100 to get a percentage instead of a decimal:
select 100 * count(case when state = 1 then state end)
/ count(case when state in (1,2) then state end) as percentage
from table_a
Count ignores nulls, and both of the case expressions default to null if their conditions are not met (you could have else null to make it explicit too).
Quick demo with a CTE for dummy data:
with table_a(state) as (
select 1 from dual connect by level <= 20
union all select 2 from dual connect by level <= 5
union all select 3 from dual connect by level <= 42
)
select 100 * count(case when state = 1 then state end)
/ count(case when state in (1,2) then state end) as percentage
from table_a;
PERCENTAGE
----------
80

Why the plsql tag? Regardless, i think what you need is:
(select count(1) from table_a where state=1) * 100 / (select count(1) from table_a where state in (1,2)) from dual

Related

Making changes on a pivot table

I have the following query:
SELECT CASE GROUPING_ID(status, CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END)
WHEN 0
THEN TO_CHAR(status)
WHEN 2
THEN 'subtotal'
ELSE 'total'
END AS status,
COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
COUNT(*) AS total
FROM (Select STATUS,USER_TYPE
FROM TRANSACTIONS tr join TRANSACTION_STATUS_CODES sc on sc.id = tr.user_type join
TRANSACTION_USER_TYPES ut on ut.id=tr.user_type
WHERE tr.status!=1 AND Tr.Update_Date BETWEEN TO_DATE('2022-01-01 00:00:00', 'yyyy-mm-dd HH24:MI:SS') AND TO_DATE('2022-11-13 23:59:59', 'yyyy-mm-dd HH24:MI:SS'))
GROUP BY CUBE(status,CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END)
HAVING GROUPING_ID(status, CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END) IN (0, 3)
OR ( GROUPING_ID(status, CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END) = 2
AND CASE WHEN status IN (2, 4, 5) THEN 1 ELSE 0 END = 1 );
which displays:
[
with inner query giving ( thats 10 out of 36rows):
anyways.. I've been asked to change the inner query to a better organized one, so I came up with the following pivot table ( which I want it to be my new inner query ):
SELECT p.*
FROM (
SELECT user_type,
status
FROM transactions
WHERE status !=1
AND Update_Date >= DATE '2022-01-01'
AND Update_Date < DATE '2022-11-14'
) PIVOT (
COUNT(*)
FOR user_type IN (1,2,3,5)
) p
ORDER BY status asc;
which gives:
what I need to try and achieve is the sum of subtotal/total of rows aswell as total of column( as I have in the first table ), and when I try to make it an inner query the outer query doesnt recognize anything..
so basically to add these:
is that achievable without changing the entire query?
or else, what is the best approach to achieve that?
thanks in advance!
CUBE does what you want in a single query and was covered in my previous answer.
You cannot use PIVOT to do what you want in a single query; instead you would need to use multiple queries joined together with UNION ALL to calculate the status, sub-total and total parts (which is likely to be much less efficient):
SELECT TO_CHAR(status) AS status,
"1",
"2",
"3",
"5",
"1" + "2" + "3" + "5" AS total
FROM table_name
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )
UNION ALL
SELECT 'SUB_TOTAL',
"1",
"2",
"3",
"5",
"1" + "2" + "3" + "5" AS total
FROM (SELECT user_type FROM table_name WHERE status IN (2,4,5))
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )
UNION ALL
SELECT 'TOTAL',
"1",
"2",
"3",
"5",
"1" + "2" + "3" + "5" AS total
FROM (SELECT user_type FROM table_name)
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )
or:
SELECT TO_CHAR(status) AS status,
COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
COUNT(*) AS total
FROM table_name
GROUP BY status
UNION ALL
SELECT 'SUB_TOTAL',
COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
COUNT(*) AS total
FROM table_name
WHERE status IN (2,4,5)
UNION ALL
SELECT 'TOTAL',
COUNT(CASE user_type WHEN 1 THEN 1 END) AS "1",
COUNT(CASE user_type WHEN 2 THEN 1 END) AS "2",
COUNT(CASE user_type WHEN 3 THEN 1 END) AS "3",
COUNT(CASE user_type WHEN 5 THEN 1 END) AS "5",
COUNT(*) AS total
FROM table_name
Which, for the sample data, (also from my previous answer):
CREATE TABLE table_name (status, user_type) AS
SELECT 2, 1 FROM DUAL CONNECT BY LEVEL <= 3 UNION ALL
SELECT 4, 1 FROM DUAL CONNECT BY LEVEL <= 13 UNION ALL
SELECT 5, 1 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 3, 1 FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 3, 5 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 0, 1 FROM DUAL CONNECT BY LEVEL <= 4 UNION ALL
SELECT 0, 5 FROM DUAL CONNECT BY LEVEL <= 8;
Both output the same as using a single query with CUBE:
STATUS
1
2
3
5
TOTAL
2
3
0
0
0
3
4
13
0
0
0
13
0
4
0
0
8
12
5
1
0
0
0
1
3
5
0
0
1
6
SUB_TOTAL
17
0
0
0
17
TOTAL
26
0
0
9
35
If you did want to do it without CUBE then you can generate extra rows before pivoting (however, I think CUBE will still be more efficient):
SELECT title AS status,
"1",
"2",
"3",
"5",
"1" + "2" + "3" + "5" AS total
FROM (
SELECT l.*, t.user_type
FROM table_name t
CROSS JOIN LATERAL (
SELECT t.status, TO_CHAR(t.status) AS title, 1 AS priority FROM DUAL UNION ALL
SELECT NULL, 'SUB_TOTAL', 2 FROM DUAL WHERE t.status IN (2,4,5) UNION ALL
SELECT NULL, 'TOTAL', 3 FROM DUAL
) l
)
PIVOT ( COUNT(*) FOR user_type IN (1,2,3,5) )
ORDER BY priority, status
Which produces the same output.
fiddle

Counting record values in Oracle

First of all please accept my apologize because of bad english. Also I'm new to this portal. I hope you'll help me for my questions.
I have two questions in Oracle.
How to find the number of '*' in each column on the table.
how to find the total number of '*' in the table.
The first question answer will be 2 2 3
The second one answer will be 7.
Thanks in advance to spend your valuable time to answer my questions!
C1 C2 C3
* 1 1
0 0 *
1 * *
* * *
Use conditional aggregation to count the stars in each column and for the total stars just add the previous totals:
SELECT COUNT(CASE c1 WHEN '*' THEN 1 END) AS c1_stars,
COUNT(CASE c2 WHEN '*' THEN 1 END) AS c2_stars,
COUNT(CASE c3 WHEN '*' THEN 1 END) AS c3_stars,
COUNT(CASE c1 WHEN '*' THEN 1 END)
+ COUNT(CASE c2 WHEN '*' THEN 1 END)
+ COUNT(CASE c3 WHEN '*' THEN 1 END) AS total_stars
FROM table_name
select sum(
case when c1='*' then 1
else 0 end) c1_cnt,
sum(
case when c2='*' then 1
else 0 end) c2_cnt,
sum(
case when c3='*' then 1
else 0 end) c3_cnt,
sum(
regexp_count(c1||c2||c3,'\*')) cnt_total_stars
from cnt_star;
C1_CNT
C2_CNT
C3_CNT
CNT_TOTAL_STARS
2
2
3
7
For the original table -
select * from cnt_star;
C1
C2
C3
*
1
1
0
0
*
1
*
*
*
*
*

Oracle - Parent - child + fill mising hierarchy levels

I have created my fiddle example here: FIDDLE
Here is also athe code from the fiddle:
CREATE TABLE T1(ID INT, CODE INT, CODE_NAME VARCHAR(100), PARENT_ID INT);
INSERT INTO T1 VALUES(100,1,'LEVEL 1', NULL);
INSERT INTO T1 VALUES(110,11,'LEVEL 2', 100);
INSERT INTO T1 VALUES(120,111,'LEVEL 3', 110);
INSERT INTO T1 VALUES(125,112,'LEVEL 3', 110);
INSERT INTO T1 VALUES(130,1111,'LEVEL 4', 120);
INSERT INTO T1 VALUES(200,2,'LEVEL 1', NULL);
INSERT INTO T1 VALUES(210,21,'LEVEL 2', 200);
INSERT INTO T1 VALUES(300,3,'LEVEL 1', NULL);
I have trouble finding the soultuin how to get from that table this result:
| CODE | CODE NAME | CODE 1 |CODE NAME 1| CODE 2 | CODE NAME 2| CODE 3 | CODE NAME 3 |
+--------+------------+--------+-----------+--------+------------+--------+-------------+
| 1 | LEVEL 1 | 11 | LEVEL 2 | 111 | LEVEL 3 | 1111 | LEVEL 4 |
| 1 | LEVEL 1 | 11 | LEVEL 2 | 112 | LEVEL 3 | 112 | LEVEL 3 |
| 2 | LEVEL 1 | 21 | LEVEL 2 | 21 | LEVEL 2 | 21 | LEVEL 2 |
| 3 | LEVEL 1 | 3 | LEVEL 1 | 3 | LEVEL 1 | 3 | LEVEL 1 |
I have tried something with connect by but that is not what I need(I think)...
The max I will ever have is 4 levels and if there are only two levels in the data then the 3rd and the 4th level should be filled wiht the values of the last existing value. The same rule is valid if there are 3 levels or 1 level.
You can use a recursive sub-query:
WITH hierarchy (
code, code_name,
code1, code_name1,
code2, code_name2,
code3, code_name3,
id, depth
) AS (
SELECT code,
code_name,
CAST(NULL AS INT),
CAST(NULL AS VARCHAR2(100)),
CAST(NULL AS INT),
CAST(NULL AS VARCHAR2(100)),
CAST(NULL AS INT),
CAST(NULL AS VARCHAR2(100)),
id,
1
FROM t1
WHERE parent_id IS NULL
UNION ALL
SELECT h.code,
h.code_name,
CASE depth WHEN 1 THEN COALESCE(t1.code, h.code) ELSE h.code1 END,
CASE depth WHEN 1 THEN COALESCE(t1.code_name, h.code_name) ELSE h.code_name1 END,
CASE depth WHEN 2 THEN COALESCE(t1.code, h.code1) ELSE h.code2 END,
CASE depth WHEN 2 THEN COALESCE(t1.code_name, h.code_name1) ELSE h.code_name2 END,
CASE depth WHEN 3 THEN COALESCE(t1.code, h.code2) ELSE h.code3 END,
CASE depth WHEN 3 THEN COALESCE(t1.code_name, h.code_name2) ELSE h.code_name3 END,
t1.id,
h.depth + 1
FROM hierarchy h
LEFT OUTER JOIN t1
ON (h.id = t1.parent_id)
WHERE depth < 4
)
CYCLE code, depth SET is_cycle TO 1 DEFAULT 0
SELECT code, code_name,
code1, code_name1,
code2, code_name2,
code3, code_name3
FROM hierarchy
WHERE depth = 4;
Which, for the sample data:
CREATE TABLE T1(ID, CODE, CODE_NAME, PARENT_ID) AS
SELECT 100, 1, 'LEVEL 1', NULL FROM DUAL UNION ALL
SELECT 110, 11, 'LEVEL 2', 100 FROM DUAL UNION ALL
SELECT 120, 111, 'LEVEL 3', 110 FROM DUAL UNION ALL
SELECT 130, 1111, 'LEVEL 4', 120 FROM DUAL UNION ALL
SELECT 200, 2, 'LEVEL 1', NULL FROM DUAL UNION ALL
SELECT 210, 21, 'LEVEL 2a', 200 FROM DUAL UNION ALL
SELECT 220, 22, 'LEVEL 2b', 200 FROM DUAL UNION ALL
SELECT 230, 221, 'LEVEL 3', 220 FROM DUAL UNION ALL
SELECT 300, 3, 'LEVEL 1', NULL FROM DUAL;
Outputs:
CODE
CODE_NAME
CODE1
CODE_NAME1
CODE2
CODE_NAME2
CODE3
CODE_NAME3
1
LEVEL 1
11
LEVEL 2
111
LEVEL 3
1111
LEVEL 4
3
LEVEL 1
3
LEVEL 1
3
LEVEL 1
3
LEVEL 1
2
LEVEL 1
21
LEVEL 2a
21
LEVEL 2a
21
LEVEL 2a
2
LEVEL 1
22
LEVEL 2b
221
LEVEL 3
221
LEVEL 3
db<>fiddle here
For sample data you posted:
SQL> select * from t1;
ID CODE CODE_NAME PARENT_ID
---------- ---------- ---------- ----------
100 1 LEVEL 1
110 11 LEVEL 2 100
120 111 LEVEL 3 110
130 1111 LEVEL 4 120
200 2 LEVEL 1
210 21 LEVEL 2 200
6 rows selected.
SQL>
an ugly (and who-knows-how-performant) query that, though, returns desired result is
with temp as
(select id, code, code_name, parent_id, level lvl,
row_number() over (partition by level order by id) rn
from t1
start with parent_id is null
connect by prior id = parent_id
),
a as
(select * from temp where lvl = 1),
b as
(select * from temp where lvl = 2),
c as
(select * from temp where lvl = 3),
d as
(select * from temp where lvl = 4)
select
a.code code1, a.code_name code_name1,
coalesce(b.code, a.code) code2, coalesce(b.code_name, a.code_name) code_name2,
coalesce(c.code, b.code, a.code) code3, coalesce(c.code_name, b.code_name, a.code_name) code_name3,
coalesce(d.code, c.code, b.code, a.code) code4, coalesce(d.code_name, c.code_name, b.code_name, a.code_name) code_name4
from a join b on b.rn = a.rn
left join c on c.rn = b.rn
left join d on d.rn = c.rn;
which results in
CODE1 CODE_NAME1 CODE2 CODE_NAME2 CODE3 CODE_NAME3 CODE4 CODE_NAME4
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 LEVEL 1 11 LEVEL 2 111 LEVEL 3 1111 LEVEL 4
2 LEVEL 1 21 LEVEL 2 21 LEVEL 2 21 LEVEL 2
What does it do?
temp CTE creates a hierarchy; additionally, row_number function numbers each row within the same level
a, b, c, d CTEs extract values belonging to their own level value (you said there can be up to 4 levels)
finally, coalesce on column names along with outer join do the job
From your example I assume you want to see one row per root key as your example is not realy a tree but a bamboo
If so this is a trivial PIVOT query - unfortunately limited to some level deep (here example for your 4 levels)
with p (ROOT_CODE, CODE, CODE_NAME, ID, PARENT_ID, LVL) as (
select CODE, CODE, CODE_NAME, ID, PARENT_ID, 1 LVL from t1 where PARENT_ID is NULL
union all
select p.ROOT_CODE, c.CODE, c.CODE_NAME, c.ID, c.PARENT_ID, p.LVL+1 from t1 c
join p on c.PARENT_ID = p.ID),
t2 as (
select ROOT_CODE, CODE,CODE_NAME,LVL from p)
select * from t2
PIVOT
(max(CODE) code, max(CODE_NAME) code_name
for LVL in (1 as "LEV1",2 as "LEV2",3 as "LEV3",4 as "LEV4")
);
ROOT_CODE LEV1_CODE LEV1_CODE_ LEV2_CODE LEV2_CODE_ LEV3_CODE LEV3_CODE_ LEV4_CODE LEV4_CODE_
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 1 LEVEL 1 11 LEVEL 2 111 LEVEL 3 1111 LEVEL 4
2 2 LEVEL 1 21 LEVEL 2
The recursive CTE calculates the ROOT_CODE required for the pivot.
I' leaving as an exercise to fill the not defined levels (with COALESCE) with the previous values as in your example.
In case (as commented) you nedd oner row for each leave key a simple solution based on CONNECT_BY_PATHis possible.
I'm using again *recursive CTEcalculating the path from *root* to the *current node* and finaly filtering in the result the *leaves* (IDthat are notPARENT_ID`)
with p ( CODE, CODE_NAME, ID, PARENT_ID, PATH) as (
select CODE, CODE_NAME, ID, PARENT_ID, to_char(CODE)||'|'||CODE_NAME PATH from t1 where PARENT_ID is NULL
union all
select c.CODE, c.CODE_NAME, c.ID, c.PARENT_ID, p.PATH ||'|'||to_char(c.CODE)||'|'||c.CODE_NAME from t1 c
join p on c.PARENT_ID = p.ID)
select PATH from p
where ID in (select ID from T1 MINUS select PARENT_ID from T1)
order by 1;
The result holds for any level deepness and is concatenated string with delimiter
PATH
----------------------------------------------
1|LEVEL 1|11|LEVEL 2|111|LEVEL 3|1111|LEVEL 4
1|LEVEL 1|11|LEVEL 2|112|LEVEL 3
2|LEVEL 1|21|LEVEL 2
3|LEVEL 1
Use substr instr to extract and coalesce for the default values.
Solution using a hierarchical query - we record the code and code_name paths, then we break them apart. Level is used to decide whether we populate data from the paths or from the leaf node. The solution assumes the codes and code names do not contain the forward-slash character (if they may, use another separator in the paths - perhaps some control character like chr(31), the unit separator character in ASCII and Unicode).
To break apart the paths, I used regexp_substr as it's easier to work with (and, moreover, I assumed all codes and code names are non-null - if they may be null, the solution can be adapted easily). If this proves to be slow, that can be changed to use standard string functions.
with
p (code, code_name, parent_id, lvl, code_pth, code_name_pth) as (
select code, code_name, parent_id, level,
sys_connect_by_path(code, '/') || ',' ,
sys_connect_by_path(code_name, '/') || ','
from t1
where connect_by_isleaf = 1
start with parent_id is null
connect by parent_id = prior id
)
select case when lvl = 1 then code
else to_number(regexp_substr(code_pth, '[^/]+', 1, 1)) end as code,
case when lvl =1 then code_name
else regexp_substr(code_name_pth, '[^/]+', 1, 1) end as code_name,
case when lvl <= 2 then code
else to_number(regexp_substr(code_pth, '[^/]+', 1, 2)) end as code_1,
case when lvl <= 2 then code_name
else regexp_substr(code_name_pth, '[^/]+', 1, 2) end as code_name_1,
case when lvl <= 3 then code
else to_number(regexp_substr(code_pth, '[^/]+', 1, 3)) end as code_2,
case when lvl <= 3 then code_name
else regexp_substr(code_name_pth, '[^/]+', 1, 3) end as code_name_2,
code as code_3,
code_name as code_name_3
from p;

(Oracle)Getting 25th number using interpolating

My goal is to get 25th number. For instance I have 4 row, such as 3,4,5 and 7.
My goal is to get 1.25th(=(4+1)0.25).
Expected result is 3.25 which is obtained by interpolating(3+0.25(4-3)).
I have tried as below.
But is there any other efficient way?
WITH DATASET AS (
SELECT 3 C1 FROM DUAL
UNION
SELECT 4 FROM DUAL
UNION
SELECT 5 FROM DUAL
UNION
SELECT 7 FROM DUAL
)
SELECT
--RNK, C1, NEXTC1-C1, FIRSTQLOCAION, FIRSTQLOCAION-RNK, C1+(NEXTC1-C1)*(FIRSTQLOCAION-RNK)
C1+(NEXTC1-C1)*(FIRSTQLOCAION-RNK)
FROM(
SELECT C1,
LEAD(C1, 1) OVER (ORDER BY C1) as NEXTC1 ,
RANK() OVER (ORDER BY C1) AS RNK,
((SUM(1) OVER (PARTITION BY NULL)) +1) * 0.25 AS FIRSTQLOCAION
FROM DATASET
)
WHERE
FIRSTQLOCAION>=RNK AND FIRSTQLOCAION<=RNK+1;
You can use analytical function as follows:
Select c1,
c1 + (
(((Count(1) over () + 1)*0.25) - 1) * (lead(c1) over (order by c1) - c1)
) as calculated_number from
From your_table t
In this solution last record will have calculated value null as lead value will be null and you will have to adjust its value as per your requirement.
If your expectation is a single number from query then usw following:
Select min(c1) +
0.25 * (min(case when rn = 2 then c1 end)
- min(case when rn = 1 then c1 end)) as calculated_number
from
(Select t.*,
Row_number() over (order by c1)
From t)
WITH t AS (
SELECT 3 C1 FROM DUAL
UNION
SELECT 4 FROM DUAL
UNION
SELECT 5 FROM DUAL
UNION
SELECT 7 FROM DUAL
)
SELECT rn,location, calculated
FROM (
Select rn, c1,
C1 +(Count(1) over () + 1)*0.25
-trunc( (Count(1) over () + 1)*0.25 ) *(lead(c1) over (order by c1) - c1) as calculated, --
trunc( (Count(1) over () + 1)*0.25 ) as location --
From (Select t.*, Row_number() over (order by c1) rn From t) ) WHERE rn=location;

how i need to pick values depending upon other values

I have a table with data shown below
no s d
100 I D
100 C D
101 C null
101 I null
102 C D
102 I null
then i'm using this query to partition
create table pinky nologging as
select no,status,dead
from(select no,status,dead,
row_number() over(partition by no order by dead desc) seq
from PINK) d
where seq = 1;
i'm getting this results
100 I D
101 C null
102 I null
but i want data like shown below
100 C D
101 I NULL
102 I NULL
i.e,
FOR I AND C COMBINATION and both d column is D then pick C
FOR I AND C COMBINATION and both d column is null then pick I
FOR I AND C COMBINATION and d column is null AND d then pick null corresponding value
Assuming that there can be only one dead is null record for each no:
with
-- you data, remove it when running the query
-- in your environment ...
pink (no, status, dead) as
(select 100, 'I', 'D' from dual union
select 100, 'C', 'D' from dual union
select 101, 'C', null from dual union
select 101, 'I', null from dual union
select 102, 'C', 'D' from dual union
select 102, 'I', null from dual
),
-- ... end of you data
--
temp as ( -- a temporary table (CTE) where we make some
-- preliminary calculations
select pink.*,
-- count rows with status = 'I' for this no
sum(case when status = 'I' then 1 else 0 end) over(partition by no) ni,
-- count rows with status = 'C' for this no
sum(case when status = 'C' then 1 else 0 end) over(partition by no) nc,
-- count rows with dead = 'D' for this no
sum(case when dead = 'D' then 1 else 0 end) over(partition by no) nd,
-- total number of rows (in case it's not always = 2)
count(*) over(partition by no) n
from pink
)
select no, status, dead
from temp
where -- pick 'C' if there's also 'I' and all dead = 'D'
status = 'C' and ni > 0 and nd = n
-- pick 'I' if there's also 'C' and all dead is null
or status = 'I' and nc > 0 and nd = 0
-- pick dead is null if there are I's and C's and
-- all other dead's = 'D'
or dead is null and ni > 0 and nc > 0 and n - nd = 1;

Resources