Oracle Query to get single result depending on values in result set - sql

WITH T1 AS SELECT DISTINCT(DETAILS) FROM ( SELECT STATUS, PREREQUISITE_NM,
(case when (STATUS='Completed' ) then 'Completed'
when (STATUS='Pending' ) then 'Pending'
when (STATUS='Failed' and PREREQUISITE_NM = 'Y') then 'Failed'
when (STATUS='Failed' and PREREQUISITE_NM = 'N') then 'Completed'
end )DETAILS FROM TABLE_LIST WHERE ID=1))
T2 AS ( SELECT DETAILS FROM T1)
Result 1 :
Pending
Failed
Completed.
Result 2:
Failed
Completed.
In above Query we see the different result set as per data available. I want to write a query in T2 Block which should give Output as :
for Pending/Failed/Completed : should give Pending as output.
For Failed/Completed : should give Failed as Output.
Is it possible to achieve this through query without using PL SQL block. like using WITH clause?
I can explain my aim such as
Lets say T1 Block is giving me result as three rows Pending,Failed, Completed then I want Pending as a Output value. If T1 Block is giving me result as Failed, Completed then I want to Failed as Output value. If T1 block is giving only Completed then Completed will be considered as a output value.

Thanks Roberto and Barbaros Ozhan, Both Answers were useful.Posting another way shared by one of my colleague.
WITH T1 AS (
SELECT
DISTINCT(DETAILS)
FROM (
SELECT STATUS, PREREQUISITE_NM,
(
CASE
WHEN (STATUS = 'Failed' ) THEN
CASE WHEN PREREQUISITE_NM = 'Y' THEN 'Failed'
WHEN PREREQUISITE_NM = 'N' THEN 'Completed'
END
ELSE
STATUS
END
)DETAILS FROM TABLE_lIST WHERE ID=1)
),
T2 AS
(
SELECT CASE
WHEN EXISTS
( SELECT DETAILS FROM T1 where DETAILS='Pending' )
THEN 'Pending'
WHEN EXISTS
( SELECT DETAILS FROM T1 WHERE DETAILS='InProgress' )
THEN 'InProgress'
WHEN EXISTS
(SELECT DETAILS FROM T1 where DETAILS='Failed' )
THEN 'Failed'
ELSE 'Completed' END as DETAILS from DUAL
)SELECT * FROM T2

You can use ROW_NUMBER() Analytic function to bring those status values in alphabetical order descendingly which would suite your need :
WITH T1 AS
(
SELECT DISTINCT (CASE
WHEN (STATUS = 'Failed' ) THEN
CASE WHEN PREREQUISITE_NM = 'Y' THEN 'Failed'
WHEN PREREQUISITE_NM = 'N' THEN 'Completed'
END
ELSE
STATUS
END) AS details
FROM TABLE_LIST
WHERE ID = 1
), T2 AS
(
SELECT T1.*, ROW_NUMBER() OVER (ORDER BY details DESC) AS rn
FROM T1
)
SELECT details
FROM T2
WHERE rn = 1

After reading your comments, I came up with this solution, that perhaps is not the best, but at the end gives the output you expect , based on that what you actually want is to transpose the rows produced in t1 to columns in t2
I used my own test, please be aware to replace the columns or modify whatever is necessary to adapt it to your own query
SQL> desc my_test
Name Null? Type
----------------------------------------- -------- ----------------------------
C1 NUMBER
C2 VARCHAR2(20)
C3 VARCHAR2(1)
SQL> select * from my_test ;
C1 C2 C3
---------- -------------------- -
1 Failed Y
2 Completed Y
1 Pending Y
1 Pending N
SQL>
So, If just got the first part of my query
SQL> WITH T1 AS (
SELECT DISTINCT c2 FROM ( SELECT c1,c2,
case when c2='Completed' then 'Completed'
when c2='Pending' then 'Pending'
when c2='Failed' and c3 = 'Y' then 'Failed'
when c2='Failed' and c3 = 'N' then 'Completed'
else c2
end FROM my_test WHERE c1=1
) )
select trim ( completed || ' ' || failed || ' ' || pending ) as result
from
(
select * from t1 pivot ( max(c2) for c2 in ( 'Completed' as Completed, 'Failed' as Failed, 'Pending' as Pending ) )
)
/ 9 10 11 12 13 14 15
RESULT
--------------------------------------------------------------
Failed Pending
Now I only need to build a case over that result ( modify it to your own requirements )
SQL> WITH T1 AS (
SELECT DISTINCT c2 FROM ( SELECT c1,c2,
case when c2='Completed' then 'Completed'
when c2='Pending' then 'Pending'
when c2='Failed' and c3 = 'Y' then 'Failed'
when c2='Failed' and c3 = 'N' then 'Completed'
else c2
end FROM my_test WHERE c1=1
) )
select
case when result = 'Completed Failed Pending' then 'Pending'
when result = 'Completed Failed' then 'Failed'
when result = 'Failed Pending' then 'Failed' -- I guess
when result = 'Completed Pending' then 'Pending' -- I guess
end as output
from (
select trim ( completed || ' ' || failed || ' ' || pending ) as result
from
(
select * from t1 pivot ( max(c2) for c2 in ( 'Completed' as Completed, 'Failed' as Failed, 'Pending' as Pending ) )
)
)
/
OUTPUT
-------
Failed
SQL>

Related

Remove the 'All' DAT value rows which has multiple DAT values for 1 ID and one of them is 'All'

How to remove the 'All' DAT value rows which has multiple DAT values for 1 ID and one of them is 'All'. However if the val has only 'All' as the DAT value we should include those rows:
Sample input:
Sample output:
Any help would be appreciated. Thanks in advance!
If you just want a select query, then window functions can be helpful:
select t.*
from (select t.*,
sum( case when data <> 'All' then 1 else 0 end) over (partition by id, val) as cnt_notall
from t
) t
where not (data = 'All' and cnt_notall > 0);
DELETE FROM
your_table
WHERE
EXISTS (
SELECT *
FROM your_table lookup
WHERE lookup.id = your_table.id
AND lookup.val = your_table.val
AND lookup.dat <> 'All'
)
AND dat = 'All'

Oracle SQL: Joining same table and getting desired output

I have a table like this
FILEID | FILENAME | STATUS
100 |Employee_06102016.txt |PASS
100 |Employee_06092016.txt |FAIL
100 |Employee_06092016.txt |MISS
101 |ABC_06092016.txt |PASS
I am reading a filename from file and passing to SQL. Lets say, i have only the file name 'Emplyee_06102016.txt' which is with PASS staus. With this, i need to join the same table and take the count of PASS and FAIL filenames which have same file id and should exclude the MISS status.
I am trying something like this below but gives count as 3 including all. I should get only 2.
SELECT COUNT (T.FILEID) FROM TABLE_NAME T, TABLE_NAME S
WHERE T.FILEID=S.FILEID
AND T.FILENAME = 'Employee_06102016.txt' AND T.STATUS IN ('PASS', 'FAIL');
Oracle Setup:
CREATE TABLE table_name ( FILEID, FILENAME, STATUS ) AS
SELECT 100, 'Employee_06102016.txt', 'PASS' FROM DUAL UNION ALL
SELECT 100, 'Employee_06092016.txt', 'FAIL' FROM DUAL UNION ALL
SELECT 100, 'Employee_06092016.txt', 'MISS' FROM DUAL UNION ALL
SELECT 101, 'ABC_06092016.txt', 'PASS' FROM DUAL;
Query:
SELECT *
FROM (
SELECT t.*,
COUNT(1) OVER ( PARTITION BY FileID ) AS num_pass_fail
FROM table_name t
WHERE status IN ( 'PASS', 'FAIL' )
)
WHERE filename = 'Employee_06102016.txt';
Output:
FILEID FILENAME STATUS NUM_PASS_FAIL
---------- --------------------- ------ -------------
100 Employee_06102016.txt PASS 2
SELECT
FILENAME,
SUM(CASE status WHEN 'PASS' THEN 1 ELSE 0 END) as "Pass Count",
SUM(CASE status WHEN 'FAIL' THEN 1 ELSE 0 END) as "Fail Count",
SUM(CASE status WHEN 'MISS' THEN 1 ELSE 0 END) as "Miss Count"
FROM
TableName
WHERE
FILENAME = 'Employee_06102016.txt'
It seems that you simply need:
SELECT COUNT (1)
FROM TABLE_NAME
WHERE FILENAME = 'Employee_06102016.txt'
AND STATUS IN ('PASS', 'FAIL');
Try this one
select cnt from (
select count(*) as cnt,
listagg(filename, ',') within group(order by filename) as filename_list from table_name
where status in ('PASS', 'FAIL') group by fileid
) where instr(filename_list, 'Employee_06102016.txt')>0;

Not getting desire SQL output with inner query

Table-1: NominationPeriods
----------------------
Id Period Status
----------------------
1 9 Unlocked
3 8 locked
----------------------
Table-2 : NominationRevisions
-------------------------------------------
Id Revision Period Status
-------------------------------------------
15 1 9 M
19 2 9 R
20 3 9 A
--------------------------------------------
Query:
SELECT Period
,nomper.STATUS
,(
CASE
WHEN (
(
SELECT (
CASE
WHEN STATUS IN ('P','A')
THEN 'TRUE'
ELSE 'FALSE'
END
)
FROM NominationRevisions
WHERE revision = (
SELECT max(revision)
FROM NominationRevisions
WHERE Period = nomper.Period
)
AND NomPeriodNbr = nomper.Period
) = 'TRUE'
)
THEN 'TRUE'
ELSE 'FALSE'
END
) AS Flag
FROM NominationPeriods nomper
Expected Output:
-------------------------------------
Period Status Flag
-------------------------------------
9 Unlocked TRUE
8 locked TRUE
-------------------------------------
Actual Output:
-------------------------------------
Period Status Flag
-------------------------------------
9 Unlocked TRUE
-------------------------------------
I want all NominationPeriods list as output having
Highest Revision of the Period is 'A' or 'P' then Flag=TRUE
If no Revision found for a period then also return Flag=TRUE
Try this:
select "Period", "Status",
Coalesce((select top 1 case when r."Status" in ('A', 'P') then 'TRUE' else 'FALSE' end from NominationRevisions r where p.Period = r.Period order by r.Revision desc), 'TRUE') "Flag"
from NominationPeriods p
Now I believe it's OK.
I expect this might work for you, using JOIN:
SELECT
np.*,
COALESCE(
IF( nr.`Status` IS NULL, 'TRUE', NULL),
IF(nr.`Status` IN ('A', 'R'), 'TRUE', NULL),
'FALSE'
) AS 'Flag'
FROM nominationperiods np
LEFT JOIN nominationrevisions nr ON
np.Period = nr.Period
AND nr.Revision = (SELECT MAX(nr.Revision)
FROM nominationrevisions nr
WHERE nr.Period = np.Period)
The join takes max revision only, and then uses coalesce() to test all conditions for true, with a fallback to false.
I rewrited your query with less cases and in a didatic manner. See this fiddle for a working example usind the data your provided.
The point is: Return all Nomination Period but only set flag to TRUE if the last revision got an A or P status.
select nomper.Period, nomper.Status
,case when
(
exists
(
select 1
from dbo.[NominationRevisions] nr
join
(
select max(nr0.Revision) as MaxRevision, nr0.Period
from dbo.[NominationRevisions] nr0
group by nr0.Period
) as nr1 on nr1.Period = nr.Period
and nr1.MaxRevision = nr.Revision
where nr.Status = 'A' or nr.Status = 'P'
)
)
then 'TRUE'
else 'FALSE'
end
from dbo.[NominationPeriods] nomper

sql Select id that matches combination of column

I have a token table
id | status
------------
1 | taken
1 | used
1 | deleted
2 | taken
2 | deleted
3 | taken
I need to count how many tokens are used ( in use or used).
If a token is taken and deleted without being used then it should not be counted.
So sql would be sth like
SELECT count(*) if the id's status is not (taken & deleted)
The desired number of used token in above example is 2 as
id 1 has been taken used and deleted -> count it
id 3 has been taken -> count it
id 2 has been taken and deleted without being used -> do not count it
A little bit verbose but efficient and still readable and maintainable:
SELECT COUNT(DISTINCT id)
FROM dbo.Token t
WHERE EXISTS
(
SELECT 1 FROM dbo.Token t1
WHERE t.id = t1.id
AND t1.status = 'used'
)
OR
(
EXISTS(
SELECT 1 FROM dbo.Token t1
WHERE t.id = t1.id
AND t1.status = 'taken'
)
AND NOT EXISTS(
SELECT 1 FROM dbo.Token t1
WHERE t.id = t1.id
AND t1.status = 'deleted'
)
)
Demo
Use aggregation and a having clause to get the list of eligible ids:
SELECT id
FROM token t
GROUP BY id
HAVING SUM(case when status = 'taken' then 1 else 0 end) > 0 or
SUM(case when status = 'used' then 1 else 0 end) > 0;
To get the count, use a subquery or CTE:
SELECT COUNT(*)
FROM (SELECT id
FROM token t
GROUP BY id
HAVING SUM(case when status = 'taken' then 1 else 0 end) > 0 or
SUM(case when status = 'used' then 1 else 0 end) > 0
) t
Try this:
SELECT SUM(CASE WHEN (CHARINDEX('used', data.status) > 0) OR (data.status = 'taken') THEN 1 ELSE 0 END) as [count]
FROM
(
SELECT DISTINCT id, (SELECT STUFF((SELECT Distinct ',' + status
FROM token a
WHERE a.id = b.id
FOR XML PATH (''))
, 1, 1, '')) as status
FROM token b
) data
Demo
You need to be able to take into account all three conditions, so a naive approach would be to just compare each three with a case statement:
WITH grouped as
(
select id from #uses group by id
)
select grouped.id,
used =
CASE WHEN used.id is not null THEN 'YES'
WHEN taken.id is not null and deleted.id is null THEN 'YES'
ELSE 'NO'
END
from grouped
left join #uses taken on grouped.id = taken.id
and taken.use_status = 'taken'
left join #uses used on grouped.id = used.id
and used.use_status = 'used'
left join #uses deleted on grouped.id = deleted.id
and deleted.use_status = 'deleted'
The case statement will stop whenever the condition is met, so you only need to WHEN's and an ELSE to meet the conditions.
This is a naive approach, though, and assumes that you only ever have one row per id and use status type. You'd have to do some additional work if that wasn't the case.
if token has been taken and used -> do not count it
SELECT
SUM(DECODE(status, 'taken', 1, 0)) +
SUM(DECODE(status, 'used', 1, 0)) -
SUM(DECODE(status, 'deleted', 1, 0))
FROM
token t
WHERE
status <> 'used' OR
EXISTS(SELECT 1 FROM token t2 WHERE t2.id = t.id and t2.status = 'deleted')
if token has been taken and used -> count it
SELECT
COUNT(1)
FROM
token t
WHERE
status = 'taken' AND
(
EXISTS(SELECT 1 FROM token t2 WHERE t2.id = t.id and t2.status = 'used') OR
NOT EXISTS(SELECT 1 FROM token t2 WHERE t2.id = t.id and t2.status = 'deleted')
)
Coming back to this question, one solution could be with using Pivot
SELECT COUNT(id)
FROM (
SELECT id, status FROM Token
) src
PIVOT
(
COUNT(status) FOR status IN ([taken], [used], [deleted])
) pvt
WHERE (taken = 1 AND deleted = 0)OR (used = 1)
DEMO

Joining 2 queries...error while pulling certain fields out

I am trying to join 2 queries & get certain columns out of the join. But I am getting an error. Can you please help me understand where I am going wrong -
SELECT X.*,Y.* FROM
(
(
SELECT
C1,C2,C3
COUNT(C4) AS CNT -- count
FROM [dbo].[Tb1]
WHERE C1 <> 0 AND -- amount not = zero
C2 = 'F' -- flag
GROUP BY C1,C2,C3
HAVING COUNT(C4) > 1
)X
INNER JOIN
(SELECT * FROM [dbo].[Tb1])Y
ON
X.C1 = Y.C1
AND X.C2 = Y.C2
AND X.C3=Y.C3
AND X.C4=Y.C4
)
The first query helps me get the duplicates & the second query will help me get the other fields out of the same table.
Thanks.
Solution #1:
SELECT X.*,Y.* FROM
--( <-- (1) comment this line
(
SELECT
C1,C2,C3, -- <-- (2) add , after C3
COUNT(C4) AS CNT -- count
FROM [dbo].[Tb1]
WHERE C1 <> 0 AND -- amount not = zero
C2 = 'F' -- flag
GROUP BY C1,C2,C3
HAVING COUNT(C4) > 1
)X
INNER JOIN
(SELECT * FROM [dbo].[Tb1])Y
ON
X.C1 = Y.C1
AND X.C2 = Y.C2
AND X.C3=Y.C3
AND X.CNT=Y.C4 <-- see anir's comment
--) <-- (3) comment this line
Or
Solution #2:
SELECT X.*, Y.*
FROM
(
SELECT
C1,C2,C3,
COUNT(C4) AS CNT -- count
FROM [dbo].[Tb1]
WHERE
C1 <> 0 AND -- amount not = zero
C2 = 'F' -- flag
GROUP BY C1,C2,C3
HAVING COUNT(C4) > 1
) X
INNER JOIN [dbo].[Tb1] Y
ON X.C1 = Y.C1
AND X.C2 = Y.C2
AND X.C3=Y.C3
AND X.CNT=Y.C4 <-- see anir's comment
Note #1: When CNT > 1 and x.C1 , y.C1 contains NULLs then X.C1 = Y.C1 <=> NULL = NULL which is evaluated to UNKNOWN if ANSI_NULLS is ON. This means that these rows will be eliminated from final resultset. The same applies to X.C2 = Y.C2 and X.C3=Y.C3.
SET ANSI_NULLS ON
SELECT CASE WHEN NULL = NULL THEN 1 ELSE 0 END AS T1
SET ANSI_NULLS OFF
SELECT CASE WHEN NULL = NULL THEN 1 ELSE 0 END AS T2
/*
T1
-----------
0
T2
-----------
1
*/
Note #2: "In a future version of SQL Server, ANSI_NULLS will always be ON and any applications that explicitly set the option to OFF will generate an error.".
Or
Solution #3:
SELECT y.*
FROM
(
SELECT x.*, COUNT(x.C4) OVER(PARTITION BY x.C1, x.C2, x.C3) AS CNT -- count
FROM [dbo].[Tb1] x
WHERE
x.C1 <> 0 AND -- amount not = zero
x.C2 = 'F' -- flag
-- AND x.C1 IS NOT NULL AND x.C2 IS NOT NULL AND x.C3 IS NOT NULL ?
) y
WHERE y.CNT > 1 AND y.CNT = y.C4