SQL JOIN WITH TWO WHERE CLAUSES - sql

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

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"

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

PIVOT combined tables with criteria in sql

I have 2 tables
I want to show the result that returns all rows that has both a work and a home number
RESULT
I have written this SQL but it shows all. How do I show only to appear those with both values in home and work number and not showing the null values. I have tried adding WHERE PHONE_NUM IS NOT NULL but it did not work. I would appreciate any help. Thanks.
WITH TABLE1 AS (
SELECT
P.ID,
P.NAMES,
P.DIGIT,
Q.NUM_TYP,
Q.PHONE_NUM
FROM
dbo.TABLE1 P
INNER JOIN dbo.TABLE2 Q
ON P.ID = Q.ID
)
SELECT *
FROM
TABLE1
PIVOT (Max(PHONE_NUM) FOR NUM_TYP IN (HOME, WORK)) R
;
You can get the results from just table 2 using conditional aggregation:
select t2.id,
max(case when t2.num_type = 'HOME' then phone_num end) as home,
max(case when t2.num_type = 'WORK' then phone_num end) as work
from dbo.TABLE2 t2
group by t2.id
having max(case when t2.num_type = 'HOME' then phone_num end) is not null and
max(case when t2.num_type = 'WORK' then phone_num end) is not null;
You can join table 1 to get other fields if you like.

Aggregating SQL results, two tables, then summarizing results

Using MSSQL
I have a table of users, and a table of products to which they are subscribed. Those subscriptions are either Free (F) or Paid (P). I have joined the tables, converted the F/P value to numeric using a case statement, and then summed up those values by user ID, the idea being that anyone with only free accounts will have a sum of 0, those with at least one paid account a value 1 or greater. I've gotten this far with the following:
SELECT t1.user_id, SUM(
CASE
WHEN t2.free_paid = 'P'
THEN 1
ELSE 0
END ) as highest
FROM users t1 INNER JOIN accounts t2
ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
GROUP BY t1.user_id
ORDER BY t1.user_id
This yields a result like:
755 2
1259 2
2031 1
3888 0
Meaning that all but 3888 have at least one paid account.
But now I would like to simply add those up somehow to get our two values, one a count of users with at least one paid account (3 in example), and a count of those with only free accounts (1 in example).
I tried declare two variables, e.g. #free and #paid and using a case statement to add to those values by wrapping that around the above and treating it as a subquery, but I am unable to get that run.
Any ideas?
Re-using the query from the question you can create a CTE (or a subquery) and aggregate the results:
;WITH CTE_UserAccounts AS (
SELECT t1.user_id, SUM(
CASE
WHEN t2.free_paid = 'P'
THEN 1
ELSE 0
END ) as highest
FROM users t1 INNER JOIN accounts t2
ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
GROUP BY t1.user_id
)
SELECT
COUNT(CASE WHEN highest > 0 THEN 1 END) AS [Paid],
COUNT(CASE WHEN highest = 0 THEN 1 END) AS [Free]
FROM
CTE_UserAccounts;
SELECT
COUNT(DISTINCT CASE WHEN t2.free_paid = 'P' THEN t1.user_id END) as atleast_one_paid,
COUNT(DISTINCT CASE WHEN t2.free_paid <> 'P' THEN t1.user_id END) as onlyfree
FROM users t1
INNER JOIN accounts t2 ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
You could just wrap around your original query and sum it up
SELECT SUM (case when highest > 0 THEN 1 else 0 END) as UsersWithPaidAccount,
SUM (case when highest = 0 THEN 1 else 0 END) as UsersWithOnlyFreeAccount
FROM (SELECT t1.user_id, SUM(
CASE
WHEN t2.free_paid = 'P'
THEN 1
ELSE 0
END ) as highest
FROM users t1 INNER JOIN accounts t2
ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
GROUP BY t1.user_id)
as DerivedTable