Difference between "on .. and" and "on .. where" in SQL Left Join? [duplicate] - sql

This question already has answers here:
What's the difference between "where" clause and "on" clause when table left join?
(6 answers)
Closed 2 years ago.
Sql statement.
1.select a.* from A a left join B b on a.id =b.id and a.id=2;
2.select a.* from A a left join B b on a.id =b.id where a.id=2;
what is the difference of this two sql statement?

create table A(id int);
create table B(id int);
INSERT INTO A VALUES(1);
INSERT INTO A VALUES(2);
INSERT INTO A VALUES(3);
INSERT INTO B VALUES(1);
INSERT INTO B VALUES(2);
INSERT INTO B VALUES(3);
SELECT * FROM A;
SELECT * FROM B;
id
-----------
1
2
3
id
-----------
1
2
3
Filter on the JOIN to prevent rows from being added during the JOIN process.
select a.*,b.*
from A a left join B b
on a.id =b.id and a.id=2;
id id
----------- -----------
1 NULL
2 2
3 NULL
WHERE will filter after the JOIN has occurred.
select a.*,b.*
from A a left join B b
on a.id =b.id
where a.id=2;
id id
----------- -----------
2 2

select a.* from A a left join B b on a.id =b.id and a.id=2;
This only uses a.id in the join condition, so records where a.id <> 2 don't get filtered out. You might get a result like this:
+------+------+
| a.id | b.id |
+------+------+
| 1 | NULL |
| 2 | 2 |
| 3 | NULL |
+------+------+
You don't select any of b's columns, but if you do, it'll be easier to understand.
select a.* from A a left join B b on a.id =b.id where a.id=2;
Now records where a.id <> 2 do get filtered out.
+------+------+
| a.id | b.id |
+------+------+
| 2 | 2 |
+------+------+

As #hvd says, the "where" clause filters rows returned by the join, so the "where" version won't return outer-joined rows (which have a.id = null).
However there is another significant difference: Even if the outer joined rows were not filtered out, there can be a massive performance boost putting the condition into the "on" clause, because the result set is made smaller earlier.
This is particularly pronounced when a series of other left joined tables follows the one with the "and" condition - you can prevent joins from even happening to the following tables for unsuitable rows and potentially chop off millions of rows from reaching the filtering ("where") stage.

I try some time ,and I know what is the reason, it only related to a priority.
select * from A a left join B b on a.id=b.id and b.id=2
this means A left join (where b.id=2) this is the condition
filter B first
Select * from A a left join B b on a.id=b.id where a.id=2
this means after join B ,then filter by a.id=2

If you think about the syntax of a SQL query, the 'AND' extends the join block (as if where parenthesis) where as the 'WHERE' defines the start of the WHERE/filtering block of the query.

As clearly explained by the #mr_eclair
what happens in both cases.
Let me tell you an easy way to remember this.
select a.*,b.*
from A a left join B b
**on** a.id =b.id ***and*** a.id=2;
Here the "AND" worked on the "ON" and it provides a condition to the joining criteria.
select a.*,b.*
from A a left join B b
on a.id =b.id
**where** a.id=2;
whereas here "WHERE" provided a condition to all the result.
To put it more clearly,
"WHERE" filter out the result set after finding the result from "SELECT" statement.
"AND" is a condition on joining the two tables.

Related

Create column that combines two columns by ifelse statementet in SQL

In SQL I am trying to combine create a column id_main2 (after a right join) that is equal the value of column id_main (coming from a) if not NULL and the value of id (coming from b) if id_main is NULL.
Below is the join code followed by the desired output. How can I create this id_main2 column?
SELECT * FROM a
RIGHT JOIN b on a.id = b.id;
id_main id boy id girl id_main2
10 1 Alex 1 Alice 10
11 2 Bruce 2 Brunet 11
NULL NULL NULL 5 Emma 5
NULL NULL NULL 6 Fabia 6
I think you just want coalesce():
select a.*, b.*,
coalesce(a.id_main, b.id)
from b left join
a
on a.id = b.id;
I strongly prefer left join to right join, so I rearranged the tables in the from clause.
You can either use coalesce()
select
coalesce(a.id_main, b.id) as id_main2
from a
right join b on a.id = b.id;
or case when
select
case when a.id is not null then a.id
else b.id end as id_main2
from a
right join b on a.id = b.id;

Oracle - Left outer join with where clause

I have a join on two tables defined as a left outer join so that all records are returned from the left hand table even if they don't have a record in the right hand table. However I also need to include a where clause but.... I still want a row from the left-hand table to be returned for each record in the left-hand table even if the condition in the where clause isn't met. Is there a way of doing this?
I am writing the query with the join condition like
SELECT A.*, B.*
FROM A
LEFT OUTER JOIN B ON A.VIN = B.VIN AND
TRUNC(a.REP_OPEN_DATE) BETWEEN TRUNC(b.CHECK_IN_DATE)+1 AND TRUNC(b.CHECK_IN_DATE)-1
above condition not returning any rows. where as below condition returns...
SELECT A.*, B.*
FROM A
LEFT OUTER JOIN B ON A.VIN = B.VIN
I need the data from the left table even if the below condition doesn't met...
TRUNC(a.REP_OPEN_DATE) BETWEEN TRUNC(b.CHECK_IN_DATE)+1 AND TRUNC(b.CHECK_IN_DATE)-1
can some one please help.
A TABLE:-
VIN | RO_OPEN_DATE
1234 | 04-NOV-13
6789 | 09-NOV-13
B TABLE
VIN | CHECK_IN_DATE
1234 | 09-NOV-13
1234 | 05-NOV-13
6789 | 20-OCT-14
6789 | 29-OCT-14
OUTPUT SHOULD BE
VIN | RO_OPEN_DATE | CHECK_IN_DATE
1234 | 04-NOV-13 | 05-NOV-13
6789 | 09-NOV-13 | NULL
Condition :-For EACH RO_OPEN_DATE for a VIN, We need to check if we have +/- 1 day of CHECK_IN_DATE from RO_OPEN_DATE.
Your explanation does not correlate with your query. You have mentioned
"However I also need to include a where clause but.... I still want a row from the left-hand table to be returned for each record in the left-hand table even if the condition in the where clause isn't met."
So I believe your query looks something like this
SELECT a.*,
b.*
FROM a
LEFT OUTER JOIN b
ON a.vin = b.vin
WHERE Trunc(a.rep_open_date) BETWEEN Trunc(b.check_in_date) + 1 AND
Trunc(b.check_in_date) - 1
In the above the LEFT OUTER JOIN will be converted into INNER JOIN due to the filtration of right table in Where clause
So as you have used in first query the right table filters should be part of JOIN condition, Which will return rows from LEFT table even though there is no matching records in RIGHT side table.
SELECT a.*,
b.*
FROM a
left outer join b
ON a.vin = b.vin
AND Trunc(a.rep_open_date) BETWEEN
Trunc(b.check_in_date) + 1 AND
Trunc(b.check_in_date) - 1
Update :
You have used between operator like 10 between 11 and 9 but it should be 10 between 9 and 11
SELECT a.*,
b.*
FROM a
left outer join b
ON a.vin = b.vin
AND CAST(a.rep_open_date as date) BETWEEN
CAST(b.check_in_date as date) - 1 AND
CAST(b.check_in_date as date) + 1
If there are no rows in B that satisfy the join, then you'll have to specify what to do with NULL values for b.CHECK_IN_DATE, e.g.,
TRUNC(a.REP_OPEN_DATE) BETWEEN TRUNC(NVL(b.CHECK_IN_DATE,a.rep_open_date))+1 AND TRUNC(NVL(b.CHECK_IN_DATE,a.rep_open_date))-1
You need to decide what to do when b.CHECK_IN_DATE is NULL, which will happen whenever you have no records in B that satisfy the join condition, because a.REP_OPEN_DATE will never be between two NULLs. I used a.rep_open_date as an example that will always succeed. You could also simply add an OR b.checkin_date_date IS NULL to your WHERE clause.

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

Query with join equivalency?

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.