Query with join equivalency? - sql

Are these two queries equivalent (assuming varying/any kinds of data in the table)? Are there any scenarios in which they would return different results?
Query 1:
select * from tablea a
left join tableb b on a.keyacol = b.keybcol
inner join tablec c on c.keyccol = b.keybcol;
Query 2:
select * from tablea a
left join (
select b.*, c.* from tableb b
inner join tablec c on c.keyccol = b.keybcol
) sub on a.keyacol = sub.keybcol;

No, they are not equivalent. Example:
CREATE TABLE a
( keya int ) ;
CREATE TABLE b
( keyb int ) ;
CREATE TABLE c
( keyc int ) ;
INSERT INTO a
VALUES
(1) ;
INSERT INTO b
VALUES
(1),(2) ;
INSERT INTO c
VALUES
(2) ;
Results:
SELECT *
FROM a
LEFT JOIN b
ON a.keya = b.keyb
INNER JOIN c
ON c.keyc = b.keyb ;
Result
----------------------
| keya | keyb | keyc |
----------------------
SELECT *
FROM a
LEFT JOIN
( SELECT b.*, c.*
FROM b
INNER JOIN c
ON c.keyc = b.keyb
) sub
ON a.keya = sub.keyb ;
Result
----------------------
| keya | keyb | keyc |
----------------------
| 1 | NULL | NULL |
----------------------
As to why this happens, a LEFT JOIN b INNER JOIN c is parsed as (a LEFT JOIN b) INNER JOIN c which is equivalent to (a INNER JOIN b) INNER JOIN c because the condition on the INNER join cancels the LEFT join.
You can also write the second query in this form - without subquery - which is parsed as a LEFT JOIN (b INNER JOIN c) because of the different placing of the ON clauses:
SELECT *
FROM a
LEFT JOIN
b
INNER JOIN c
ON c.keyc = b.keyb
ON a.keya = b.keyb ;
Result
----------------------
| keya | keyb | keyc |
----------------------
| 1 | NULL | NULL |
----------------------

They're not equivalent.
Essentially, there are four scenarios here, for records on A:
corresponding records on B and C exist;
corresponding records exist on B but not C;
corresponding records exist on C but not B;
corresponding records don't exist on B or C.
Both queries will return the same values for scenario 1.
However, they will return different values for the other scenarios - the inner join on a value of B to a value C in the first query means that you will be attempting to join a null to a value of C in the other scenarios.

The INNER JOIN keyword return rows when there is at least one match in
both tables/selections. If there are rows in tableb that do not have
matches in tablec, those rows will NOT be listed.
The LEFT JOIN keyword returns all the rows from the left table
(tablea), even if there are no matches in the right table (tableb or
sub selection).
Since the left join returns all rows, the two queries might differ if there aren't any correspondent matches in the inner join from the first query. Those rows won't be selected in the first query but will appear in the second since it's used a left join.
Another thing that migh differ was the columns. But since the select * from you will select all colums from all tables/selections and:
Query 1 returns all columns from tablea, tableb and tablec
Query 2 returns all columns from tablea and selection sub (returns all columns from tableb and tablec) = returns all columns from tablea, tableb and tablec
this isn't a problem.
So NO, they are equivalent.

Related

Could i use right outer join and inner join together?

I'm trying to get the data from 3 tables but i think i need to use inner join (to join TABLE1 and TABLE2) and right outer join because i need all information from TABLE3 but i don't know how to do it. I tried this:
select a.TABLE1_ID, e.TABLE2_ID,f.TABLE3_ID
from TABLE1 as a, TABLE2 as e, TABLE3 as f
where a.TABLE1_ID = e.TABLE2_ID and a.TABLE1_COL = f.TABLE3_ID
UNION ALL
SELECT f.TABLE3_ID, NULL
FROM TABLE3 as f
WHERE NOT EXISTS (
SELECT * FROM TABLE1 as a
WHERE a.TABLE1_COL = f.TABLE3_ID);
But i get the following error:
The operands of a set operator or a VALUES clause do not have the same number of columns.. SQLCODE=-421, SQLSTATE=42826, DRIVER=4.19.56
How could i do it? Is it possible?
**EDIT:
The following is the result that i would like to get:
a.TABLE1_ID | e.TABLE2_ID | f.TABLE3_ID
----------------------------------------
AFR123 | AFR123 | MM145FR123
AFR124 | AFR124 | MM145FR124
NULL | NULL | MM145FR125
NULL | NULL | MM145FR126
You are describing LEFT JOINs:
select a.TABLE1_ID, e.TABLE2_ID, f.TABLE3_ID
from TABLE3 f left join
TABLE1 a
ON a.TABLE1_ID = f.TABLE3_ID left join
TABLE2 e
on a.TABLE1_ID = e.TABLE2_ID ;
As a note: If there is a row in a that matches f but has no matching row in e, then this keeps the values from a. If you using INNER JOIN followed by RIGHT JOIN, you won't have the values in the intermediate table. Usually having such values is desired.

Return all inactive items from Table B for each item in Table A

Not sure if I can explain this well in words..
I want to find all items in Table B that have ALL items inactive for each item in Table A. Let's say Table A just has column "item" and Table B has columns "item" and "active"
Table A Table B
A A | 1
A A | 1
A B | 1
B B | 0
B C | 0
B C | 0
C D | 0
C E | 1
D
E
F
In that example, it should return C and D.
I managed to join tables A and B together with a group by clause but not sure where to go from there. Tried looking around and only found answers where the other table's value doesn't exist so you can use "NOT IN" but that doesn't seem to work here.
Any help would be appreciated!
You can join the tables and use the HAVING clause to make the comparison like this:
SELECT ta.Item
FROM TableA tA
LEFT JOIN TableB tB
ON tA.Item=tB.Item
GROUP BY tA.Item
HAVING SUM(tB.Inactive)=COUNT(tb.Inactive)
This would give you a distinct list of Items in TableA
The above query assumes 1 is Inactive and 0 is Active. If your data is opposite (which it looks like it is), you could instead say:
SELECT ta.Item
FROM TableA tA
LEFT JOIN TableB tB
ON tA.Item=tB.Item
GROUP BY tA.Item
HAVING SUM(tB.Inactive)=0
This would also return Item F as it doesn't have a value in table b to SUM. Just flip the LEFT JOIN to an INNER JOIN if you don't want Item F to return.
If you need to return back all instances in TableA, you could use a subquery and join to that:
SELECT ta.Item
FROM TableA tA
LEFT JOIN (SELECT ITEM, SUM(ACTIVE) Sum0 FROM TableB GROUP BY ITEM)tB
ON tA.Item=tB.Item
WHERE tB.Sum0 = 0
/*or tB.Sum0 is null would give you Item F*/
Use NOT EXISTS:
select distinct a.item
from table_A a
where not exists (select 1 from table_B b where b.item = a.item and b.status = 1);
I believe you would just need to join the tables together on table name and filter to only value of 0 on the active column.
SELECT B.*
FROM TableA A INNER JOIN TableB B ON A.item = B.item
WHERE B.active = 0
Does that get you what you need?

SQL - not sure how to join tables

I'm trying to join two tables like this:
Table A
ID Value1
1 A
2 B
3 C
Table B
ID Value2
1 A
3 B
4 C
Result should be:
ID Value1 Value2
1 A A
2 B null
3 C B
4 null C
I.e. join Table A to Table B on ID. If ID doesn't exist in Table A, add the ID from Table B.
The closest I've come is:
SELECT
a.ID, a.Value1, b.Value2
FROM
TableA a
OUTER JOIN
TableB b ON a.ID = b.ID
That gives me the new rows from TableB, but the ID is null.
How can I accomplish this?
You are very close, you just need a little push in the right direction:
SELECT COALESCE(a.ID, B.ID) As ID, a.Value1, b.Value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
The COALESCE function returns the first parameter it gets that is not null. since this is a full outer join, a.id will be null on one row and b.id would be null on a different row.
Try this:
SELECT *
FROM TableA A
FULL OUTER JOIN TableB B
ON A.ID = B.ID;
Just a note: you should not name your tables in SQL with spaces in them.
Remember the basic for joining different tables
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name=table2.column_name;
For your case:
SELECT a.value1, b.value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
remember full outer join
The FULL OUTER JOIN keyword returns all rows from the table (tableA) and from the table (tableB) and the FULL OUTER JOIN keyword combines the result of both LEFT and RIGHT joins.

Join on 3 tables

i have currently 3 tables :
Table A
Table B
Table C
There is a link between A & B and a link between B & C (A-B-C).
The thing is that :
It is possible to have a row in A but no not in B
It is possible to have a row in B but not in A
It is possible to have a row in B but not in C
In the end i would like to have a query which could give me the following row (where X represent the ID of the corresponding table) :
TableA|TableB|TableC
X | X | X
X | null | null
null | X | X
X | X | null
I managed to have the case with TableA & TableB with the following query :
SELECT A.ID, B.ID
FROM TABLEA A
LEFT JOIN TABLEB B on (join condition)
UNION
SELECT A.ID,B.ID
FROM TABLE B
LEFT JOIN TABLEA A on (join condition)
Thank you for any help you may provide
What you need is a FULL OUTER JOIN, however, you have tagged your post with sybase - it depends what you mean by that. Sybase ASE doesn't support FULL OUTER JOIN syntax, but SQL Anywhere does.
If I understood it correctly then a FULL OUTER JOIN should do your work :
SELECT a.id,b.id,c.id
FROM TableA a
FULL OUTER JOIN TableB b on a.id = b.id
FULL OUTER JOIN TableC c on COALESCE(a.id,b.id) = c.id
SQL Fiddle

sql join, left join . records not shown if there's criteria for right table

Table a fields:
id_a
Table b fields:
id_b
id_b2
filed_b
filed_b2
Table c fields:
id_c
filed_c
table a data:
id_a
----
1
2
table b data:
id_b id_b2 filed_b
---- ----- -------
1 1 1
2 2 100
table c data:
id_c filed_c
---- ---------
1 adfa11111
2 dfdf22222
join
a join b on id_a=id_b
b join c on id_b2=id_c
The goal is to get all table a data and associated filed_c data.
Criterion is: if filed_b=100, list filed_c. otherwise leave filed_c null.
Problem: used left join, if no criteria on the right table, it's fine. But once there's a criteria on right table, the records not exist in right table won't show up.
The trick to get a where condition to work with a left outer join is to put the criteria in the join clause. If you checked against b.filed_b in the where clause you exclude the rows where the values is null which it is when there is no match in table b.
Something like this in your case.
select *
from a
left outer join b
on a.id_a = b.id_b and
b.filed_b = 100
left outer join c
on b.id_b2 = c.id_c