oracle sql error Case When Then Else - sql

SELECT *
FROM FirstTable
WHERE RowProcessed = 'N'
AND (
CASE
WHEN EXISTS(SELECT top 1 FROM SecondTable)
THEN 1
ELSE EXISTS(
SELECT SecondTable.RowProcessed
FROM SecondTable
WHERE FirstTable.Key = SecondTable.Key
AND SecondTable.RowProcessed = 'Y'
)
END
)
AND OtherConditions
Case When then else in where clause. Not sure about the syntax.
I would like to verify there are rows in the SecondTable and if there are rows check for another condition, that is basicallly what I want to do.

Perhaps this is what you want? If there are rows in SecondTable, then do the second EXISTS:
SELECT * FROM FirstTable
WHERE RowProcessed = 'N'
AND (NOT EXISTS (SELECT 1 from SecondTable)
OR EXISTS (SELECT 1 FROM SecondTable
WHERE FirstTable.Key = SecondTable.Key
and SecondTable.RowProcessed = 'Y'))
AND OtherConditions

Related

Oracle SQL - implement logic based on CASE expression result

I have following query which returns me of percentage of rows with at least one NULL in any of columns:
SELECT
(SUM(CASE WHEN tablea.test IS NULL OR tableb.test IS NULL THEN 1 ELSE NULL END)/7000)*100) "at least one NULL (%)"
FROM tablea
FULL OUTER JOIN tableb
ON
tablea.test = tableb.test
WHERE ROWNUM < 7000;
Query works fine to me and I am getting valid result.
But I need to do further action in tablec, based on percentage calculated in my SELECT statement. If percentage of rows with NULL is below 30% I need to insert "YES" string inside table "tablec" column "resultcol", how can I implement such a logic?
Is it for example possible to store SELECT statement result in some temporary variable which will be used in another SQL query?
You can use:
INSERT INTO tablec (resultcol)
SELECT 'YES'
FROM tablea
FULL OUTER JOIN tableb
ON tablea.test = tableb.test
WHERE ROWNUM < 7000
HAVING COUNT(CASE WHEN tablea.test IS NULL OR tableb.test IS NULL THEN 1 END)
< COUNT(*) * 0.3;
db<>fiddle here
Update:
If you want to insert a row if it does not exist or change the row if it does exist then:
MERGE INTO tablec dst
USING (
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM tablea
FULL OUTER JOIN tableb
ON tablea.test = tableb.test
WHERE ROWNUM < 7000
HAVING COUNT(CASE WHEN tablea.test IS NULL OR tableb.test IS NULL THEN 1 END)
< COUNT(*) * 0.3
)
THEN 'YES'
ELSE 'NO'
END AS resultcol
FROM DUAL
) src
ON (1 = 1)
WHEN NOT MATCHED THEN
INSERT (resultcol) VALUES (src.resultcol)
WHEN MATCHED THEN
UPDATE SET resultcol = src.resultcol;
Or, to just update it:
UPDATE tablec
SET resultcol = CASE
WHEN EXISTS (
SELECT 1
FROM tablea
FULL OUTER JOIN tableb
ON tablea.test = tableb.test
WHERE ROWNUM < 7000
HAVING COUNT(
CASE
WHEN tablea.test IS NULL
OR tableb.test IS NULL
THEN 1
END
) < COUNT(*) * 0.3
)
THEN 'YES'
ELSE 'NO'
END;
db<>fiddle here

SQL AND to see if two flags are set in two different tables?

SELECT 1 FROM
(SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') as X,
(SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y') as Y
I'm making a query to see if two flags are set in two tables, where 'parentid' and 'id' are both primary keys. The query should return a row only if both flags are set to 'Y', or return nothing otherwise, then I do stuff with that result in my backend code.
I've tested this and it works but I feel like it looks wonky and could be optimized. Any ideas?
To get what You want:
SELECT 1
FROM mytable1 AS a, mytable2 AS b
WHERE a.parentid = 'ID1' AND a.flag = 'Y'
AND b.id = 'ID2' AND b.flag = 'Y'
But in fact, I would prefer a query with LEFT JOIN, which always gives one row, like this:
SELECT CASE WHEN a.flag = 'Y' AND b.flag = 'Y' THEN 1 ELSE 0 END AS result
FROM TABLE ( VALUES 1 ) AS always(present)
LEFT JOIN mytable1 AS a ON a.parentid = 'ID1'
LEFT JOIN mytable2 AS b ON b.id = 'ID2'
Your query is fine (although I would use CROSS JOIN. However, I would prefer a row with a specific value. I would phrase that as:
SELECT (CASE WHEN EXISTS (SELECT 1 FROM mytable1 WHERE parentid = 'ID1' AND flag = 'Y') AND
EXISTS (SELECT 1 FROM mytable2 WHERE id = 'ID2' AND flag = 'Y')
THEN 1 ELSE 0
END) as flag
You may need from dual, depending on your database.
It better to use JOIN instead of doing subqueries
SELECT mytable1.parentid, mytable2.id
FROM mytable1
JOIN mytable2 ON mytable2.flag = "Y" AND mytable1.flag = "Y"

Select query 0=0 Vs not exists

I have a table contains 100 million rows and I found the below query (A), which is taking time. I am thinking to change it to (B), is it the same?
Is this query (A) :
SELECT *
FROM tab1
WHERE code = 1
AND TYPE = 'A'
AND 0 = (SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y')
Similar to this (B) ?
SELECT *
FROM tab1
WHERE code = 1
AND TYPE = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
AND ROWNUM = 1)
I would recommend writing the query as:
SELECT t1.*
FROM tab1 t1
WHERE t1.code = 1 AND
t1.TYPE = 'A' AND
NOT EXISTS (SELECT 1
FROM tab1 tt1
WHERE tt1.code = t1.code AND
tt1.tr_type = 'APPROVE'
tt1.security = 'Y'
);
The changes are:
Table aliases are introduced.
All columns are qualified.
The rownum = 1 condition is removed.
The latter is between redundant and dangerous. EXISTS/NOT EXISTS is already looking for any row that matches. Including rownum could affect the optimizer.
Your first version has to find all matches in order to calculate the count. That can be much more expensive that determining that there are no matches or finding the first one.
For performance, you want indexes on tab1(code, tr_type, security) and tab1(code, type).
Yes same query.
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND 0 = (SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y')
If we will take output of inner query in the above query
SELECT Count(1)
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
I am assuming , Record with code = 1 , TR_TYPE = 'APPROVE' and security = 'Y' is present. So output will be 1. (Assumption is only one matching record is present).
SELECT *
FROM tab1
WHERE code =1
AND type='A'
AND 0=1 (replacing the result)
So this will not return any data as 0 is never equal to 1.
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1
WHERE code = 1
AND tr_type = 'APPROVE'
AND security = 'Y'
AND rownum = 1)
Now checking the above query, inner query return 1 as the data with the where clause is present so result will be
SELECT *
FROM tab1
WHERE code =1
AND type='A'
AND NOT EXISTS (1)
Exists(If any record found) = true and in our case Exists(1) = true so
Not Exist (1) = false. Which denotes to false as it has 1 record , so it will also not return the data.
So if you are asking that both query will return same output. So It will return same output.
Yes the output will be same because its just the interchangeable way to use it however in terms of time complexity the second one is optimised.
Yes they are the same, however i would explicitly put an alias on the table being used in the non exists and alias the columns being compared to.Eg:
SELECT *
FROM tab1
WHERE code = 1
AND type = 'A'
AND NOT EXISTS (SELECT 1
FROM tab1 b
WHERE b.code = 1
AND b.tr_type = 'APPROVE'
AND b.security = 'Y'
AND rownum = 1)

How to return true if two same columns in two different tables have values? SQL

Back on here with another question for y'all!
So we have two Pivot tables, Table_A and Table_B. Both tables have the same columns.
I was trying to write a query that'll go through each row and return true ONLY AND ONLY IF the record with the same ID has a value in the 'Value' field.
So for example, Table_A has 'sdnbfsj' value for record ID3 and Table_B has a value under the 'Value' field for the same record ID.
The query should be able to validate that the same cell in both tables have values and if they do, then return true. If the tables only had three records in total, this would have returned true but notice that record ID 5 has a value in Table_A but record ID 5 is NULL in Table_B. SO the query should return false.
This is what I have so far:
SELECT source_column from
(
select source_column from Table_A WHERE Value_Inserted=1
EXCEPT
select source_column from Table_B WHERE Value_Inserted=1
)X
WHERE source_column IN
(
'Col_1'
,'COl_2'
,'Col_3'
,'Col_4'
)
You should be able to do this by first Joining the two tables on id and then counting based on your condition. Then... return "true" or "false"
SELECT CASE WHEN SUM(CASE WHEN t1.value IS NOT NULL AND t2.value IS NOT NULL THEN 1 ELSE 0 END) = Count(*) THEN 'True' ELSE 'False' END as test
FROM table_a as t1
INNER JOIN table_b as t2 ON
t1.id = t2.id
Syntax may differ depending on what RDBMS you use.
I think you want:
select min(case when a.value is not null and a.value is null then 0
when a.value is null a and a.value is not null then 0
else 1
end) as flag
from a join
b
on a.class = b.class and a.source_col = b.source_col;
This treats 1 as true and 0 as false.
Depends on the DBMS that you're using ;
For Oracle use nvl2 function :
select nvl2(a.value, 1, 0) * nvl2(b.value, 1, 0)
from Table_A a
inner join Table_B b
on (a.ID = b.ID);
For MSSQL convert or cast functions might be used :
select convert(int, ( case when IsNull(a.value,'0') = '0' then '0'
else '1'
end ))
*
convert(int, ( case when IsNull(b.value,'0') = '0' then '0'
else '1'
end )) as result
from Table_A a
inner join Table_B b
on (a.ID = b.ID);
For MySQL use IfNull function :
select ( case when IfNull(a.value,'0') = '0' then '0'
else '1'
end )
*
( case when IfNull(b.value,'0') = '0' then '0'
else '1'
end ) as result
from Table_A a
inner join Table_B b
on (a.ID = b.ID);
For PostGreSQL use coalesce function :
select cast( ( case when coalesce(a.value,'0') = '0' then '0'
else '1'
end ) as int )
*
cast( ( case when coalesce(b.value,'0') = '0' then '0'
else '1'
end ) as int ) as result
from Table_A a
inner join Table_B b
on (a.ID = b.ID);
All of them give the following results
result
0
0
1
0
0

SQL JOIN WITH TWO WHERE CLAUSES

I would like to implement the following SQL query : suppose using JOIN clause, due to now it's running quite slow:
SELECT ID_USER, NICK
FROM TABLE1
WHERE ID_USER IN
(
SELECT ID_INDEX1
FROM TABLE2
WHERE ID_INDEX2 = '2'
)
AND ID_USER NOT IN
(
SELECT ID_INDEX2
FROM TABLE2
WHERE ID_INDEX1 = '2' AND GO ='NO'
)
ORDER BY NICK ASC
You could do the "including" part with INNER JOIN and the "excluding" part with a "LEFT JOIN" + filtering:
SELECT DISTINCT t1.ID_USER, t1.NICK
FROM TABLE1 t1
INNER JOIN TABLE2 t2IN
ON t1.ID_USER = t2IN.ID_INDEX1
AND t2IN.ID_INDEX2 = '2'
LEFT JOIN TABLE2 t2OUT
ON t1.ID_USER = t2OUT.ID_INDEX2
AND t2OUT.ID_INDEX1 = '2'
AND t2OUT.GO = 'NO'
WHERE t2OUT.ID_INDEX IS NULL
ORDER BY t1.NICK ASC
Assuming that you want to filter by ID_INDEX1 in both cases (see my comment on your question), you can:
count the number of rows per user in table2 with value = 2
count the number of rows per user in table2 with value = 2 and go = 'NO'
return only those for which the first count is greater than 0 and the second count equals 0
i.e.:
select * from (
select
id_user,
nick,
sum(case when table2.id_index2 = '2' then 1 else 0 end) as count2_overall,
sum(case when table2.id_index2 = '2' and go = 'NO' then 1 else 0 end) as count2_no
from table1
join table2 on table1.id_user = table2.id_index1
group by id_user, nick
)
where count2_overall > 0 and count2_no = 0