Postgress how to query with not in for multiple column - sql

I want to select from table C all items ID where column A "item_a_id" and column B "item_b_id" not in result of another query.
currently I use this query twice , i didnt find a way how to query it without the using same query twice.
This is my query:
SELECT * from table_c c
WHERE c.item_a_id NOT IN
(
SELECT a.item_id,b.item_id
FROM table_a a
JOIN table_b b on a.item_id = b.item_id
)
AND c.item_b_id NOT IN
(
SELECT a.item_id,b.item_id
FROM table_a a
JOIN table_b b on a.item_id = b.item_id
)
and this is how I want to improve it (ofc this is not sql syntax and just an example)
SELECT * from table_c c
WHERE c.item_a_id AND c.item_b_id NOT IN
(
SELECT a.item_id,b.item_id
FROM table_a a
JOIN table_b b on a.item_id = b.item_id
)

You need to enclose the columns on the left hand side in parentheses:
WHERE (c.item_a_id, c.item_b_id)
NOT IN (SELECT a.item_id,b.item_id
FROM table_a a
JOIN table_b b on a.item_id = b.item_id)
But typically NOT EXISTS conditions are faster than NOT IN
WHERE NOT EXISTS (SELECT *
FROM table_a a
JOIN table_b b on a.item_id = b.item_id
WHERE a.item_id = c.item_a_id
AND b.item_id = c.item_b_id)

If I understand correctly, you should be able to just do two consecutive left joins to the A and B tables. A valid match, then, is one for which neither table has any join match.
SELECT *
FROM table_c c
LEFT JOIN table_a a
ON c.item_a_id = a.item_id
LEFT JOIN table_b b
ON c.item_b_id = b.item_id
WHERE
a.item_id IS NULL AND
b.item_id IS NULL;
By the way, the above query is specifically called a left anti-join.

Related

Conditional join in SQL Server dependent on other table values

I need make a decision which table should be use in join statement depend on values in another table
I tried using CASE and COALESCE but can't achieve any success.
TableA has A and B and C and many other columns
TableB has ID and NAME columns
TableC has ID and NAME columns
My select statement is;
Select A.D, A.E, A.F From TableA A
If A.E = 1 then the following join should be used
left outer join TableB B ON A.B = B.ID
and B.NAME should be returned in the select statement
If A.E = 2 then the following join should be used
left outer join TableC C ON A.B = C.ID
and C.NAME should be returned in the select statement
Just add your conditions to the joins, and then use a case statement to pull the correct field to your result set e.g.
select A.D, A.E, A.F
, case when B.[Name] is not null then B.[Name] else C.[Name] end [Name]
from TableA A
left outer join TableB B ON A.B = B.ID and A.E = 1
left outer join TableC C ON A.B = C.ID and A.E = 2
Join tablea with the union of tableb with an extra column with value 1 and tablec with an extra column with value 2 and apply the conditions in the ON clause:
select
a.D, a.E, a.F, u.NAME
from tablea a
left join (
select *, 1 col from tableb
union all
select *, 2 col from tablec
) u on a.B = u.id and a.E = u.col

How do I run the following query?

I have to table A and B.
The relationship is one to many.
In table B have foreign key from A.
TABLE A:
... fields
TABLE B:
f = foreignkey(A)
How to get all A without B ?
The query below is not working.
select TABLE_A.id, COUNT(TABLE_B.f)
from TABLE_A JOIN TABLE_B
ON (TABLE_A.id = TABLE_B.f)
GROUP BY TABLE_A.id HAVING COUNT(TABLE_B.f) = 0;
Use not exists?
select a.*
from table_a a
where not exists (select 1
from table_b b
where a.id = b.f
);
Or not in. Or left join with where.
Change JOIN to LEFT JOIN :
TABLE_A.id, COUNT(TABLE_B.f)
from TABLE_A
LEFT JOIN TABLE_B ON (TABLE_A.id = TABLE_B.f)
GROUP BY TABLE_A.id
HAVING COUNT(TABLE_B.f) = 0;
You can do a LEFT JOIN and take only the rows that have NULL in the column TABLE_B.f,
meaning that there is no TABLE_B.f to match TABLE_A.id:
SELECT TABLE_A.id
FROM TABLE_A LEFT JOIN TABLE_B
ON TABLE_A.id = TABLE_B.f
WHERE TABLE_B.f IS NULL;
assuming that TABLE_A.id is unique in TABLE_A,
otherwise use
SELECT DISTINCT TABLE_A.id
You can use the left join and check the nullity on right table.
SELECT TABLE_A.id,count(*)
FROM TABLE_A LEFT JOIN TABLE_B
ON TABLE_A.id = TABLE_B.f
WHERE TABLE_B.f IS NULL
group by TABLE_A.id;

Controlling join order in combining left join and inner join in Impala SQL

With this kind of data
create table table_a as select 1 as id1;
insert into table_a values (2),(3);
create table table_b as select 1 as id1, 'a' as id2;
insert into table_b values (1,'b');
create table table_c as select 'a' as id2;
I have the following kind of join in Impala sql:
select *
from table_a as a
left join table_b as b
on b.id1 = a.id1
left join table_c as c
on c.id2 = b.id2
yielding this result
"id1","id1","id2","id2"
1,1,b,
1,1,a,a
2,,,
3,,,
I would like the second join to be inner join instead of left join:
select *
from table_a as a
left join table_b as b
on b.id1 = a.id1
join table_c as c /* <- How to process this join first without using inner queries? */
on c.id2 = b.id2
and get this result:
"id1","id1","id2","id2"
1,1,a,a
2,,,
3,,,
Thus, I would like the inner join of table_b and table_c to take place first and only after to do the left join between table_a and (table_b inner joined to table_b).
Is possible to determine the join order in such manner without using inner queries?
With the help from #jarlh, I realized left-to-right processing of joins and then found that it is possible to use RIGHT joins:
select *
from table_c as c
join table_b as b
on b.id2 = c.id2
right join table_a as a
on a.id1 = b.id1;
in order to get the desired result:
"id2","id1","id2","id1"
a,1,a,1
,,,2
,,,3

sql - multiple layers of correlated subqueries

I have table A, B and C
I want to return all entries in table A that do not exist in table B and of that list do not exist in table C.
select * from table_A as a
where not exists (select 1 from table_B as b
where a.id = b.id)
this gives me the first result of entries in A that are not in B. But now I want only those entries of this result that are also not in C.
I tried flavours of:
select * from table_A as a
where not exists (select 1 from table_B as b
where a.id = b.id)
AND
where not exists (select 1 from table_C as c
where a.id = c.id)
But that isnt the correct logic. If there is a way to store the results from the first query and then select * from that result that are not existent in table C. But I'm not sure how to do that. I appreciate the help.
Try this:
select * from (
select a.*, b.id as b_id, c.id as c_id
from table_A as a
left outer join table_B as b on a.id = b.id
left outer join table_C as c on c.id = a.id
) T
where b_id is null
and c_id is null
Another implementation is this:
select a1.*
from table_A as a1
inner join (
select a.id from table_A
except
select b.id from table_B
except
select c.id from table_c
) as a2 on a1.id = a2.id
Note the restrictions on the form of the sub-query as described here. The second implementation, by most succinctly and clearly describing the desired operation to SQL Server, is likely to be the most efficient.
You have two WHERE clauses in (the external part of) your second query. That is not valid SQL. If you remove it, it should work as expected:
select * from table_A as a
where not exists (select 1 from table_B as b
where a.id = b.id)
AND
not exists (select 1 from table_C as c -- WHERE removed
where a.id = c.id) ;
Tested in SQL-Fiddle (thnx #Alexander)
how about using LEFT JOIN
SELECT a.*
FROM TableA a
LEFT JOIN TableB b
ON a.ID = b.ID
LEFT JOIN TableC c
ON a.ID = c.ID
WHERE b.ID IS NULL AND
c.ID IS NULL
SQLFiddle Demo
One more option with NOT EXISTS operator
SELECT *
FROM dbo.test71 a
WHERE NOT EXISTS(
SELECT 1
FROM (SELECT b.ID
FROM dbo.test72 b
UNION ALL
SELECT c.ID
FROM dbo.test73 c) x
WHERE a.ID = x.ID
)
Demo on SQLFiddle
Option from #ypercube.Thank for the present;)
SELECT *
FROM dbo.test71 a
WHERE NOT EXISTS(
SELECT 1
FROM dbo.test72 b
WHERE a.ID = b.ID
UNION ALL
SELECT 1
FROM dbo.test73 c
WHERE a.ID = c.ID
);
Demo on SQLFiddle
I do not like "not exists" but if for some reason it seems to be more logical to you; then you can use a alias for your first query. Subsequently, you can re apply another "not exists" clause. Something like:
SELECT * FROM
( select * from tableA as a
where not exists (select 1 from tableB as b
where a.id = b.id) )
AS A_NOT_IN_B
WHERE NOT EXISTS (
SELECT 1 FROM tableC as c
WHERE c.id = A_NOT_IN_B.id
)

Aliasing derived table which is a union of two selects

I can't get the syntax right for aliasing the derived table correctly:
SELECT * FROM
(SELECT a.*, b.*
FROM a INNER JOIN b ON a.B_id = b.B_id
WHERE a.flag IS NULL AND b.date < NOW()
UNION
SELECT a.*, b.*
FROM a INNER JOIN b ON a.B_id = b.B_id
INNER JOIN c ON a.C_id = c.C_id
WHERE a.flag IS NOT NULL AND c.date < NOW())
AS t1
ORDER BY RAND() LIMIT 1
I'm getting a Duplicate column name of B_id. Any suggestions?
The problem isn't the union, it's the select a.*, b.* in each of the inner select statements - since a and b both have B_id columns, that means you have two B_id cols in the result.
You can fix that by changing the selects to something like:
select a.*, b.col_1, b.col_2 -- repeat for columns of b you need
In general, I'd avoid using select table1.* in queries you're using from code (rather than just interactive queries). If someone adds a column to the table, various queries can suddenly stop working.
In your derived table, you are retrieving the column id that exists in table a and table b, so you need to choose one of them or give an alias to them:
SELECT * FROM
(SELECT a.*, b.[all columns except id]
FROM a INNER JOIN b ON a.B_id = b.B_id
WHERE a.flag IS NULL AND b.date < NOW()
UNION
SELECT a.*, b.[all columns except id]
FROM a INNER JOIN b ON a.B_id = b.B_id
INNER JOIN c ON a.C_id = c.C_id
WHERE a.flag IS NOT NULL AND c.date < NOW())
AS t1
ORDER BY RAND() LIMIT 1
First, you could use UNION ALL instead of UNION. The two subqueries will have no common rows because of the excluding condtion on a.flag.
Another way you could write it, is:
SELECT a.*, b.*
FROM a
INNER JOIN b
ON a.B_id = b.B_id
WHERE ( a.flag IS NULL
AND b.date < NOW()
)
OR
( a.flag IS NOT NULL
AND EXISTS
( SELECT *
FROM c
WHERE a.C_id = c.C_id
AND c.date < NOW()
)
)
ORDER BY RAND()
LIMIT 1