Left Outer join and an additional where clause - sql

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 on a field from the right-hand table, 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?

Yes, put the condition (called a predicate) in the join conditions
Select [stuff]
From TableA a
Left Join TableB b
On b.Pk = a.Pk
-- [Put your condition here, like this]
And b.Column = somevalue
The reason this works is because the query processor applies conditions in a where clause after all joins are completed, and the final result set has been constructed. So, at that point, a column from the a table on the outer side of a join that has null in a a column you have established a predicate on will be excluded.
Predicates in a join clause are applied before the two result sets are "joined". At this point all the rows on both sides of the join are still there, so the predicate is effective.

You just need to put the predicate into the JOIN condition. Putting it into the WHERE clause would effectively convert your query to an inner join.
For Example:
...
From a
Left Join b on a.id = b.id and b.condition = 'x'

You can use
WHERE (right_table.column=value OR right_table.column IS NULL)
This will return all rows from table 1 and table 2, but only where table 1 does not have a corresponding row in table 2 or the corresponding row in table 2 matches your criteria.

SELECT x.fieldA, y.fieldB
FROM x
LEFT OUTER JOIN (select fieldb, fieldc from Y where condition = some_condition)
ON x.fieldc = y.fieldc

select *
from table1 t1
left outer join table2 t2 on t1.id = t2.id
where t1.some_field = nvl(t2.some_field, t1.some_field)
UPD: errr... no. this way:
select *
from table1 t1
left outer join table2 t2 on t1.id = t2.id
where some_required_value = nvl(t2.some_field, some_required_value)
nvl is an Oracle syntax which replaces first argument with second in case it is null (which is common for outer joins). You can use ifnull or coalesce for other databases.
Thus, you compare t2.some_field with your search criteria if it has met join predicate, but if it has not, then you just return row from table1, because some_required_value compared to itself will always be true (unless it is null, however - null = null yields null, neither true not false.

Related

Allays return value from left table in join

I have 2 tables
A and B
A has cols(AKey, val1, val2)
B has Cols(BKey,Akey, ValX, valY)
i have the following query
select a.Val1,a.Val2,b.ValX
from A
Left Join B on a.AKey = b.Akey
where a.Akey ={someValue}
and ((b.valY ={aDifferentVal}) or (b.valY is NULL))
The situation is that i always want to return the values from table A.
and this works when {aDifferentVal} exists in the the join, it also works when there are no values in table B for the Join, However when there are values in table be for the Join but none of these are {aDifferentVal} then the query return nothing, and i still want the values from table A.
How can i achieve this?
Just move the condition on the left joined table from the where clause to the on clause of the join - otherwise they become mandatory, and rows where they are not fullfilled are filered out (here this removes rows that match but whose valy does not match {adifferentval}):
select a.val1,a.val2,b.valx
from a
left join b
on b.akey = a.akey
and b.valy = {adifferentval}
where a.akey = {somevalue}
Move the conditions on the second table to the on clause:
select a.Val1,a.Val2,b.ValX
from A Left Join
B
on a.AKey = b.Akey and (b.valY ={aDifferentVal})
where a.Akey = {someValue}
Filtering in the where clause (sort of) turns the outer join into an inner join. Your version is slightly better, because it is checking for NULL. However, the rows that match are:
The A values that have no match in B at all.
The A values that match the condition you specify.
What gets filtered out are A values that match a row in B but none of the matches have the condition you specify.

Left Outer Join of Same Table (self-join; not first table) Not Returning Null Values

My LEFT OUTER JOIN clause on a table (not the first table) on itself (self-join too) is not returning null values, which skews my SELECT statements. The query is written (table names inconsequential):
Select
SUM(CASE WHEN table2.date =‘day’ and table4.columnX =‘5’ then table3.value1 END),
SUM(CASE WHEN table2.date=’day’ and table4.columnX IS NULL then table3.value2 END)
FROM table1
INNER JOIN table2 on ...
INNER JOIN table3 on ...
LEFT OUTER JOIN table4 on table4.columnX=’5’
In which the first SUM(CASE WHEN) statement uses on one value in table4.columnX - when it is equal to '5' - and the second SUM(CASE WHEN) statement uses all other values - whenever it is not equal to '5'.
As it stands, the query is only returning results where table4.columnX='5', and not where table4.columnX is equal to everything else. As such, it appears the LEFT OUTER JOIN is not returning all null values for table4.columnX<>'5'. I think this may be because the join is written incorrectly. As a note, there are no fields in table4 that can be joined to fields in other tables, so it has to be a self-join of some sort (I believe). Help is appreciated - thank you!
What a LEFT OUTER JOIN will do is take all the rows from the "left table" and conditionally join rows from the "right table". If for a specific row in the "left table", there is no matching row in the "right table", then your "right data values" for the joined row will be NULL.
Your join condition is strange, though, since it doesn't reference another table. If that's really what you want, then you should change it to a CROSS JOIN:
Old: LEFT OUTER JOIN table4 on table4.columnX=’5’
New: CROSS JOIN table4 on table4.columnX=’5’
This will join all rows in table4 where columnX = 5 to each row in your preceding query above. Not sure if that's what you're looking for...

SQL Different between Left join on... and Left Join on..where

I have two sql to join two table together:
select top 100 a.XXX
,a.YYY
,a.ZZZ
,b.GGG
,b.JJJ
from table_01 a
left join table_02 b
on a.XXX = b.GGG
and b.JJJ = "abc"
and a.YYY between '01/08/2009 13:18:00' and '12/08/2009 13:18:00'
select top 100 a.XXX
,a.YYY
,a.ZZZ
,b.GGG
,b.JJJ
from table_01 a
left join table_02 b
on a.XXX = b.GGG
where b.JJJ = "abc"
and a.YYY between '01/08/2009 13:18:00' and '12/08/2009 13:18:00'
The outcome of them is different but I don't understand the reason why.
I would be grateful if I can get some help here.
Whenever you are using LEFT JOIN, all the conditions about the content of the right table should be in the ON clause, otherwise you are effectively converting your LEFT JOIN to an INNER JOIN.
The reason for that is that when a LEFT JOIN is used, all the rows from the left table will be returned. If they are matched by the right table, the values of the matching row(s) will be returned as well, but if they are not matched with any row on the right table, then the right table will return a row of null values.
Since you can't compare anything to NULL (not even another NULL) (Read this answer to find out why), you are basically telling your database to return all rows that are matched in both tables.
However, when the condition is in the ON clause, Your database knows to treat it as a part of the join condition.

Changing the ON condition order results in different query results?

Will there be any difference if I change the order from this to the next one in the last line ESPECIALLY when I use left join or left outer join? SOme people confuse me that it might have differnet value when we change order, I reckon they themselves aren't sure about this.
Or, if we change the order, under what situations such as right outer, right, left, left outer joins the query result differs?
It makes no difference which side you put criteria on when an = is being used.
Table order matters in the case of LEFT JOIN and RIGHT JOIN, but criteria order does not.
For example:
SELECT *
FROM Table1 a
LEFT JOIN Table2 b
ON a.ID = b.ID
Is equivalent to:
SELECT *
FROM Table2 a
RIGHT JOIN Table1 b
ON a.ID = b.ID
But not equivalent to:
SELECT *
FROM Table2 a
LEFT JOIN Table1 b
ON a.ID = b.ID
Demo: SQL Fiddle

AND Statements in JOIN vs WHERE

I am trying to understand why the following happens (example code below).
-- Returns 1,000
SELECT COUNT(*)
FROM TABLE_ONE t1
WHERE t1.FIELD_ONE = 'Hello';
-- Returns 1,000
SELECT COUNT(*)
FROM TABLE_ONE t1
LEFT OUTER JOIN TABLE_TWO t2 ON t2.TABLE_ONE_ID = t1.ID
WHERE t1.FIELD_ONE = 'Hello'
AND t2.FIELD_TWO = 'Goodbye';
-- Returns 83,500
SELECT COUNT(*)
FROM TABLE_ONE t1
LEFT OUTER JOIN TABLE_TWO t2 ON (t2.TABLE_ONE_ID = t1.ID AND t2.FIELD_TWO = 'Goodbye')
WHERE t1.FIELD_ONE = 'Hello';
I understand that left outer joins will always include the left value even if a right value is not found, etc. However, I thought that adding additional conditions to JOIN clauses would restrict what is done in the JOIN.
So for example if I had entry A from TABLE_ONE, then it would look for a TABLE_TWO value that met the 2 conditions. If one wasn't found, it would just be blank. But that entry A would only appear once in the result set. So I am confused why I am getting so many more results then what is actually in the original TABLE_ONE query.
UPDATE
To explain more what I am trying to understand. The resulting query (not using the COUNT) results in something like this.
Table_One_Name Table_Two_Value
+++++++++++++++++++++++++++++++++++
Entry A Goodbye
Entry A
Entry A
Entry A
Entry B Goodbye
Entry B
I don't understand why the JOIN is adding rows when the 2 conditions are not met.
Where clause
filters the records, first case has an additional condition AND t2.FIELD_TWO = 'Goodbye' which filters more records(about 73,500 records)
-- Returns 1,000
SELECT COUNT(*)
FROM TABLE_ONE t1
LEFT OUTER JOIN TABLE_TWO t2 ON t2.TABLE_ONE_ID = t1.ID
WHERE t1.FIELD_ONE = 'Hello'
AND t2.FIELD_TWO = 'Goodbye';
Left outer join brings all the records even it does not have a match
In your second case AND t2.FIELD_TWO = 'Goodbye this is part of the left outer join
so it brings matching and non matching records
The WHERE condition is applied after the JOIN condition, and hence removes all the records containing NULL in t2.FIELD_TWO, thus effectively denying the purpose of the "OUTER" part of the JOIN.