Simplifying code with nested JOINs in WHERE clause - sql

I'm writing an SQL statement in PostgreSQL where I'm JOINing data from different tables that are each connected by foreign keys on their ids. Table b has a field a_id which relates to the id of table a and so on.
My problem is that I want to reuse a value from the joined table in a WHERE clause without having to do all the JOINs again, like this:
SELECT *
FROM a
INNER JOIN b ON b.a_id = a.id
INNER JOIN c ON c.b_id = b.id
WHERE a.id = 3
AND a.x =
(SELECT c.y
FROM a
INNER JOIN b ON b.a_id = a.id
INNER JOIN c ON c.b_id = b.id
WHERE a.id = 3
AND c.id = 5)
I bet there's a simpler solution for this snippet that I'm just not realising. I'll be glad if anybody can help me out.

I don't have a silver bullet answer which simplifies your query, but CTEs certainly could make it a bit easier on the eyes:
WITH cte AS (
SELECT *
FROM a
INNER JOIN b ON b.a_id = a.id
INNER JOIN c ON c.b_id = b.id
WHERE a.id = 3
)
SELECT *
FROM cte
WHERE x IN (SELECT y FROM cte WHERE c_id = 5);
My aliases or column names may be off, and you may need to tidy up the CTE a bit before it would actually work for you.

You can use window functions for this:
SELECT . . .
FROM (SELECT a.*, b.*, c.*, -- should list the columns explicitly
MAX(c.y) FILTER (WHERE c.id = 5) OVER () as y_5
FROM a INNER JOIN
b
ON b.a_id = a.id INNER JOIN
c
ON c.b_id = b.id
WHERE a.id = 3
) abc
WHERE abc.x = abc.y_5;

I hope the below-mentioned query will help you.
SELECT *
FROM a
INNER JOIN b ON b.a_id = a.id
INNER JOIN c ON c.b_id = b.id
WHERE a.id = 3 and c.id =5 and a.x = c.y

Related

Right join vs left join, which table is left vs right?

Given
select *
from a
left join b
on a.id = b.id
is table a left and table b right?
Would that be equivalent to
Select *
from a
right join b
on b.id = a.id
because I switched left and right while flipping the ON clause? Or is a still left because it came first and b is right because it's the thing we're joining?
Thank you.
No. "left" and "right" refer to the ordering of the tables in the FROM clause. So these are equivalent:
select *
from a left join
b
on a.id = b.id
select *
from b right join
a
on a.id = b.id
These two on clauses do exactly the same thing:
on a.id = b.id
on b.id = a.id
They do not affect the results at all.

Is subtraction of inner join from full outer join some type of "join"?

Given two tables, is there a name for the result of subtracting inner join from full outer join, both on the same condition? Is it a type of "join"? Thanks.
It is not a type of join in SQL. You can write it as:
select . . .
from a full join
b
on a.id = b.id
where a.id is null or b.id is null;
If you are looking for ids that are in only one table, it can be more efficient to do:
select a.id
from a
where not exists (select 1 from b where b.id = a.id)
union all
select b.id
from b
where not exists (select 1 from a where a.id = b.id);

Triple Join In SQL

How can I get the red section of the following Venn diagram in SQL
Thanks for your help!
I would think first of except:
select c.*
from c
except
select a.*
from a
except
select b.*
from b;
My next thought would be not exists:
select c.*
from t
where not exists (select 1 from a where a.id = c.id) and
not exists (select 1 from b where b.id = c.id);
Using Joins you can get any portion of Ven diagram, Somethink like below for your scenario.
select c.*
from TableC c
left join TableA a on a.id = c.id
left join TableB b on b.id = c.id
Where a.id is null and b.id is null -- Records which does not match in both tables
Well there is gonna be a bit of handwaving considering you are not giving actual table structures, but something like this:
Select c.name
From c
Where not exists (select 1 from b where b.id = c.id)
And not exists (select 1 from a where a.id = c.id);
You can use NOT IN (assuming id is the common attribute):
SELECT c.id FROM c
WHERE c.id NOT IN (SELECT a.id FROM a)
AND c.id NOT IN (SELECT b.id FROM b);

SQL summations with multiple outer joins

I have tables a, b, c, and d whereby:
There are 0 or more b rows for each a row
There are 0 or more c rows for each a row
There are 0 or more d rows for each a row
If I try a query like the following:
SELECT a.id, SUM(b.debit), SUM(c.credit), SUM(d.other)
FROM a
LEFT JOIN b on a.id = b.a_id
LEFT JOIN c on a.id = c.a_id
LEFT JOIN d on a.id = d.a_id
GROUP BY a.id
I notice that I have created a cartesian product and therefore my sums are incorrect (much too large).
I see that there are other SO questions and answers, however I'm still not grasping how I can accomplish what I want to do in a single query. Is it possible in SQL to write a query which aggregates all of the following data:
SELECT a.id, SUM(b.debit)
FROM a
LEFT JOIN b on a.id = b.a_id
GROUP BY a.id
SELECT a.id, SUM(c.credit)
FROM a
LEFT JOIN c on a.id = c.a_id
GROUP BY a.id
SELECT a.id, SUM(d.other)
FROM a
LEFT JOIN d on a.id = d.a_id
GROUP BY a.id
in a single query?
Your analysis is correct. Unrelated JOIN create cartesian products.
You have to do the sums separately and then do a final addition. This is doable in one query and you have several options for that:
Sub-requests in your SELECT: SELECT a.id, (SELECT SUM(b.debit) FROM b WHERE b.a_id = a.id) + ...
CROSS APPLY with a similar query as the first bullet then SELECT a.id, b_sum + c_sum + d_sum
UNION ALL as you suggested with an outer SUM and GROUP BY on top of that.
LEFT JOIN to similar subqueries as above.
And probably more... The performance of the various solutions might be slightly different depending on how many rows in A you want to select.
SELECT a.ID, debit, credit, other
FROM a
LEFT JOIN (SELECT a_id, SUM(b.debit) as debit
FROM b
GROUP BY a_id) b ON a.ID = b.a_id
LEFT JOIN (SELECT a_id, SUM(b.credit) as credit
FROM c
GROUP BY a_id) c ON a.ID = c.a_id
LEFT JOIN (SELECT a_id, SUM(b.other) as other
FROM d
GROUP BY a_id) d ON a.ID = d.a_id
Can also be done with correlated subqueries:
SELECT a.id
, (SELECT SUM(debit) FROM b WHERE a.id = b.a_id)
, (SELECT SUM(credit) FROM c WHERE a.id = c.a_id)
, (SELECT SUM(other) FROM d WHERE a.id = d.a_id)
FROM a

Joining many tables together using key from first table for all joins?

I would like to do something like this but can't get it to work:
SELECT A.*,B.*,C.* FROM tableA A LEFT JOIN
B ON A.ID = B.ID
C ON A.ID = C.ID
I.e. I need to use a field from the first table for all joins as tableB and tableC don't
has any fields to link them together.
Can this be done?
you missed a join !
SELECT A.*,B.*,C.* FROM tableA A
LEFT JOIN
B ON A.ID = B.ID
left join
C ON A.ID = C.ID
Try this one -
SELECT *
FROM dbo.tableA A
LEFT JOIN dbo.B ON A.ID = B.ID
LEFT JOIN dbo.C ON A.ID = C.ID