Looking for best way to execute Yes/No Query check in select statement - sql

I was wondering if anyone could recommend the best way to execute this. I will introduce you to what I'm working on.
I've written a select query with some sub-queries which gets order records, I have a number business logic that these orders need to meet so that they come up on the report.
Additionally I've added a nested case statement which helps me determine is the business logic is met and it simply returns a Yes or a No. So far all looks great!
E.G.
Above is just a sample result for one order (29817). What I need to do next is only show Order_No when NOYESCHECK returns all YES's.
Nested Case statement:
(case when sm.supply_code='Project Inventory' and
(select po.order_no
from purchase_order_line_all po
where po.contract = sm.contract
and po.part_no = sm.part_no
and po.activity_seq = sm.activity_seq
and po.project_id = sm.project_id
and po.state in ('Closed','Arrived','Recieved') order by po.date_entered desc fetch first 1 row only) is not null then 'YES'
when sm.supply_code='Invent Order' and
( select sum(QTY_ONHAND - QTY_RESERVED)
from inventory_part_in_stock ipis
where ipis.contract = sm.contract
and ipis.part_no = sm.part_no
and ipis.QTY_ONHAND - ipis.QTY_RESERVED > '0'
and ipis.project_id is null
and ipis.AVAILABILITY_CONTROL_ID not in ('QUARANTINE','RD','TRANSIT','PRE SCRAP')
) is not null then 'YES'
else 'NO' end)NoYesCheck
What would be the best way to achieve this? I have tried using ALL operator but it didn't work quite as expected. What I tried with ALL operator:
and 'YES' = ALL (case when sm.supply_code='Project Inventory' and
(select po.order_no
from purchase_order_line_all po
where po.contract = sm.contract
and po.part_no = sm.part_no
and po.activity_seq = sm.activity_seq
and po.project_id = sm.project_id
and po.state in ('Closed','Arrived','Recieved') order by po.date_entered desc fetch first 1 row only) is not null then 'YES'
when sm.supply_code='Invent Order' and
( select sum(QTY_ONHAND - QTY_RESERVED)
from inventory_part_in_stock ipis
where ipis.contract = sm.contract
and ipis.part_no = sm.part_no
and ipis.QTY_ONHAND - ipis.QTY_RESERVED > '0'
and ipis.AVAILABILITY_CONTROL_ID not in ('QUARANTINE','RD','TRANSIT','PRE SCRAP')
and ipis.project_id is null
) is not null then 'YES'
else 'NO' end)
It seemed to return only lines with 'YES' in my check but the purpose here is:
If check is done per order and returns at least one 'No' then do not show the order. So in above image this order was never meant to show up as a result in my query but it did. So I'm a little stuck.
Any help would be appreciated. Let me know if I need to provide more info.
Thanks,
Kasia

You can use your NOYESCHECK column in a subselect within the where clause combined with a NOT IN check.
Psuedo code:
select
--main query columns
from data_source
where key_column not in (
select distinct
key_column
from (
select
key_column,
noyescheck_column
from data_source
where noyescheck_column = 'NO'
)
)

Would this help? See comments within code.
SQL> with
2 -- this is what your query currently returns
3 test (order_no, component_part, noyescheck) as
4 (select 29817, 100, 'NO' from dual union all
5 select 29817, 101, 'YES' from dual union all
6 --
7 select 30000, 200, 'YES' from dual union all
8 select 30000, 201, 'YES' from dual union all
9 --
10 select 40000, 300, 'NO' from dual
11 ),
12 -- find ORDER_NOs whose NOYESCHECK = YES only
13 yess as
14 (select order_no
15 from test
16 group by order_no
17 having min(noyescheck) = max(noyescheck)
18 and min(noyescheck) = 'YES'
19 )
20 -- return only ORDER_NOs that satisfy condition
21 select t.*
22 from test t join yess y on y.order_no = t.order_no;
ORDER_NO COMPONENT_PART NOY
---------- -------------- ---
30000 200 YES
30000 201 YES
SQL>

Related

Oracle: Handling Null In Case Statement

I have a huge query used within a case-when block.
My query looks like this:
SELECT 'TEST' FROM DUAL WHERE 1=1 AND EXISTS(
SELECT
CASE
WHEN EXISTS
(Select 1 from dual where 1=2)
THEN 1
ELSE
(Select 1 from dual where 1=2)
END
FROM DUAL);
I want to execute my select-statement only if the case-when statement returns a record. However, it always prints 'Test' because this code always return a NULL:
SELECT
CASE
WHEN EXISTS
(Select 1 from dual where 1=2)
THEN 1
ELSE
(Select 1 from dual where 1=2)
END
So basically I want to print "TEST" only if no record (or null value) is returned. How can I achieve this?
A row with one column that has a NULL value is not the same as a non-existing row. So, you cannot do exactly what you want using EXISTS. One method is to do:
SELECT 'TEST'
FROM DUAL
WHERE 1 = 1 AND
1 = (SELECT CASE WHEN EXISTS(Select 1 from dual where 1=2)
THEN 1
ELSE 0
END
FROM DUAL
);
That is, look for a particular value, rather than check for the existence of a row.
We can use the following to handle the NULL
CASE TRIM(Your Field) IS NULL
THEN 'The value you want to show/ print'

Trying to aggregate string column by choosing 1 of several values

I have a dataset that I want to aggregate based on a string column. Dataset is basically:
system status
-------------------
PRE1-SYS1 SUCCESS
PRE1-SYS2 SUCCESS
PRE2-SYS1 RUNNING
PRE2-SYS2 SUCCESS
PRE3-SYS1 SUCCESS
PRE3-SYS2 <blank>
Basically, I want this to become:
system status
-------------------
PRE1 SUCCESS
PRE2 RUNNING
PRE3 RUNNING
I have the sql needed to trim down the system values to PRE1, and PRE2, but I'm not sure how to aggregate the string function so that a system is:
only SUCCESS, then status is SUCCESS
only nulls, then status is PENDING
any other combination, then RUNNING (SUCCESS/RUNNING, SUCCESS/null, RUNNING/null)
I've looked at LISTAGG but I don't think it applies.
Here is an SQL query you could use:
select regexp_substr(system, '^[^-]*') as prefix,
case
when count(status) = 0 then 'PENDING'
when count(*) = count(case when status = 'SUCCESS' then 1 end) then 'SUCCESS'
else 'RUNNING'
end as status
from mytable
group by regexp_substr(system, '^[^-]*')
with
inputs ( system, status ) as (
select 'PRE1-SYS1', 'SUCCESS' from dual union all
select 'PRE1-SYS2', 'SUCCESS' from dual union all
select 'PRE2-SYS1', 'RUNNING' from dual union all
select 'PRE2-SYS2', 'SUCCESS' from dual union all
select 'PRE3-SYS1', 'SUCCESS' from dual union all
select 'PRE3-SYS2', '' from dual
),
prep ( system, flag ) as (
select substr(system, 1, instr(system, '-') - 1),
case status when 'SUCCESS' then 0
when 'RUNNING' then 1 else 2 end
from inputs
)
select system,
case when min(flag) = 2 then 'PENDING'
when max(flag) = 0 then 'SUCCESS'
else 'RUNNING' end as status
from prep
group by system
order by system;
Output:
SYSTEM STATUS
--------- -------
PRE1 SUCCESS
PRE2 RUNNING
PRE3 RUNNING
I would approach it by ranking the responses. For example set a value to the most desired to the least desired results:
SUCCESS = 1
RUNNING = 2
<blank> = 3
PENDING = 3
Then select a min based on that.
select xval = case status when 'Success' then 1
when 'Running' then 2
when 'Pending' then 3
else 3
end
Use a nested sub select on the value you get here so you only get one record per system.
select System, Min(Xval)
then display the 1 as Success
the 2 as Running
and 3 as Pending
It is hard to do in text format, easier to do with numbers. The numbers you assign to the string are important because they determine when you have multiple values which single one you return in your final query.
Another alternative. Practically, this ends up being very similar to #trincot's solution, I'm just separating the logic for getting the counts from the logic that interprets those counts. If your logic gets more complicated in the future, this may be a bit more flexible.
with
inputs ( system, status ) as (
select 'PRE1-SYS1', 'SUCCESS' from dual union all
select 'PRE1-SYS2', 'SUCCESS' from dual union all
select 'PRE2-SYS1', 'RUNNING' from dual union all
select 'PRE2-SYS2', 'SUCCESS' from dual union all
select 'PRE3-SYS1', 'SUCCESS' from dual union all
select 'PRE3-SYS2', '' from dual
),
/* The cnts CTE counts how many rows relate to a SYSTEM,
how many of those are SUCCESS, and how many are NULL.
*/
cnts( system, num_rows, num_success, num_null ) as (
select substr(system,1,instr(system, '-')-1) system,
count(*),
sum(case when status = 'SUCCESS' then 1 else 0 end),
sum(case when status is null then 1 else 0 end)
from inputs
group by substr(system,1,instr(system, '-')-1)
)
/* Using the counts from the CTE, we can implement whatever logic we
want
*/
select system,
(case when num_rows = num_success then 'SUCCESS'
when num_rows = num_null then 'PENDING'
else 'RUNNING'
end) status
from cnts

Can Oracle PL/SQL CASE statement include a SELECT query?

I'm trying to do something similar to this:
CASE
WHEN number IN (1,2,3) THEN 'Y' ELSE 'N' END;
Instead I want to have a query in the place of the list, like so:
CASE
WHEN number IN (SELECT num_val FROM some_table) THEN 'Y' ELSE 'N' END;
I can't seem to get this to work. Also, here is an example of the query.
SELECT number, (CASE
WHEN number IN (SELECT num_val FROM some_table) THEN 'Y' ELSE 'N' END) AS YES_NO
FROM some_other_table;
Yes, it's possible. See an example below that would do what you are intending. The difference is that it uses EXISTS instead of IN.
SELECT a.number,
(CASE WHEN EXISTS (SELECT null FROM some_table b where b.num_val = a.number)
THEN 'Y'
ELSE 'N'
END) AS YES_NO
FROM some_other_table a;
EDIT:
I confess: I like the answers given by the others better personally.
However, there will be a difference between this query and the others depending on your data.
If for a value number in the table some_other_table you can have many matching entries of num_val in the table some_table, then the other answers will return duplicate rows. This query will not.
That said, if you take the left join queries given by the others, and add a group by, then you won't get the duplicates.
I suggest using an OUTER JOIN instead of trying to use a subquery in a CASE expression:
SELECT t.NUMBER,
CASE
WHEN s.NUM_VAL IS NOT NULL THEN 'Y'
ELSE 'N'
END AS YES_NO
FROM SOME_OTHER_TABLE t
LEFT OUTER JOIN SOME_TABLE s
ON s.NUM_VAL = t.NUMBER
Best of luck.
Seems like you just need to join the tables and do a decode.
with x as
(
select 1 as num from dual
union
select 2 as num from dual
union
select 3 as num from dual
),
y as
(
select 1 as num from dual
union
select 2 as num from dual
union
select 4 as num from dual
)
select x.num, decode(y.num, null, 'N','Y') as yes_no
from x
left outer join y on (x.num = y.num)
Output:
NUM YES_NO
1 Y
2 Y
3 N
You can use subquery in case statement:
select
case dummy
when 'X' then (select 'TRUE' from dual)
else 'FALSE'
end TEST
from dual;
TEST
TRUE
select
case (select 'XXX' from dual)
when 'XXX' then 'TRUE'
else 'FALSE'
end TEST
from dual;
TEST
TRUE

Sql query to find count with a difference condition and total count in the same query

Here is a sample table I have
Logs
user_id, session_id, search_query, action
1, 100, dog, A
1, 100, dog, B
2, 101, cat, A
3, 102, ball, A
3, 102, ball, B
3, 102, kite, A
4, 103, ball, A
5, 104, cat, A
where
miss = for the same user_id and same session id , if action A is not followed by action B its termed a miss.
Note: action B can happen only after action A has happened.
I am able to find the count of misses for each unique search_query across all users and sessions.
SELECT l1.search_query, count(l1.*) as misses
FROM logs l1
WHERE NOT EXISTS
(SELECT NULL FROM logs l2
WHERE l1.user_id = l2.user_id
AND l1.session_id = l2.session_id
AND l1.session_id != ''
AND l2.action = 'B'
AND l1.action = 'A')
AND l1.action='A'
AND l1.search_query != ''
GROUP BY v1.search_query
order by misses desc;
I am trying to find the value of miss_percentage=(number of misses/total number of rows)*100 for each unique search_query. I couldn't figure out how to find the count with a condition and count without that condition in the same query. Any help would be great.
expected output:
cat 100
kite 100
ball 50
One way to do it is to move the EXISTS into the count
SELECT l1.search_query, count(case when NOT EXISTS
(SELECT 1 FROM logs l2
WHERE l1.user_id = l2.user_id
AND l1.session_id = l2.session_id
AND l1.search_query = l2.search_query
AND l2.action = 'B'
AND l1.action = 'A') then 1 else null end
)*100.0/count(*) as misses
FROM logs l1
WHERE l1.action='A'
AND l1.search_query != ''
GROUP BY l1.search_query
order by misses desc;
This produces the desired results, but also zeros if no misses were found. This can be removed with a HAVING clause, or postprocessing.
Note I also added the clause l1.search_query = l2.search_query that was missing, since otherwise it was counting kite as succeeded, since there is a row with B in the same session.
I think you just need to use case statements here. If I have understood your problem correctly .. then the solution would be something like this -
WITH summary
AS (
SELECT user_id
,session_id
,search_query
,count(1) AS total_views
,sum(CASE
WHEN action = 'A'
THEN 1
ELSE 0
END) AS action_a
,sum(CASE
WHEN action = 'B'
THEN 1
ELSE 0
END) AS action_b
FROM logs l
GROUP BY user_id
,session_id
,search_query
)
SELECT search_query
,(sum(action_a - action_b) / sum(action_a)) * 100 AS miss_percentage
FROM summary
GROUP BY search_query;
You can allways create two queries, and combine them into one with a join. Then you can do the calculations in the bridging (or joining) SQL statement.
In MS-SQL compatible SQL this would be:
SELECT ActiontypeA,countedA,isNull(countedB,0) as countedB,
(countedA-isNull(countedB,0))*100/CountedA as missed
FROM (SELECT search_query as actionTypeA, count(*) as countedA
FROM logs WHERE Action='A' GROUP BY actionType
) as TpA
LEFT JOIN
(SELECT search_query as actionTypeB, count(*) as countedB
FROM logs WHERE Action='B' GROUP BY actionType
) as TpB
ON TpA.ActionTypeA = TpB.ActiontypeB
The LEFT JOIN is required to select all activities (search_query) from the 'A' results, and join them to only those from the 'B' results where a B is available.
Since this is very basic SQL (and well optimized by SQL engines) I'd suggest to prevent WHERE EXISTS as much as possible. The IsNull() function is an MS-SQL function to force a NULL value into the int(0) value which can be used in a calculation.
Finally you could filter on
WHERE missed>0
to get the final result.

Select Distinct Attribute and Print out Count of another even when the count is 0

I don't quite know how I should describe the problem for title, but here's my question.
I have a table named hello with two columns named time and state.
Time | State
Here's an example of the data I have
1 DC
1 VA
1 VA
2 DC
2 MD
3 MD
3 MD
3 VA
3 DC
I would like to get all the possible time and the count of "VA" (0 if "VA" doesn't appear at the time)
The output would look like this
Time Number
1 2
2 0
3 1
I tried to do
SELECT DISTINCT time,
COUNT(state) as Number
FROM hello
WHERE state = 'VA'
GROUP BY time
but it doesn't seem to work.
This is a conditional aggregation:
select time, sum(case when state = 'VA' then 1 else 0 end) as NumVA
from hello
group by time
I want to add that you should never use distinct when you have a group by. The two are redundant. Distinct as a keyword is not even needed in the SQL language; semantically, it is just shorthand for grouping by all the columns.
SELECT TIME,
SUM(CASE WHEN State = 'VA' THEN 1 ELSE 0 END)
FROm tableName
GROUP BY Time
SQLFiddle Demo
One rule of thumb is to get your counts first and put them into a temp for use later.
See below:
Create table temp(Num int, [state] varchar(2))
Insert into temp(Num,[state])
Select 1,'DC'
UNION ALL
Select 1,'VA'
UNION ALL
Select 1,'VA'
UNION ALL
Select 2,'DC'
UNION ALL
Select 2,'MD'
UNION ALL
Select 3,'MD'
UNION All
Select 3,'MD'
UNION ALL
Select 3,'VA'
UNION ALL
Select 3,'DC'
Select t.Num [Time],t.[State]
, CASE WHEN t.[state] = 'VA' THEN Count(t.[State]) ELSE 0 END [Number]
INTO #temp2
From temp t
Group by t.Num, t.[state]
--drop table #temp2
Select
t2.[time]
,SUM(t2.[Number])
From #temp2 t2
group by t2.[time]