Allays return value from left table in join - sql

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.

Related

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.

SQL - Need to conditionally join based on value in TableB column

I need to know the rows in TABLE A that have join records in TABLE B based a column value in TABLE B, but I also need to return rows in which a row in TABLE A has no match in TABLE B.
It seems like I need a LEFT JOIN and a LEFT OUTER JOIN, so I'm not sure what to do there. I understand how to do each, but don't understand how to do them together.
The schema looks like:
TABLE_A
pk
TABLE_B
pk
a_fk
some_value
I need the joined rows where Table_A has no join record in Table_B OR Table_A has a join record row in Table_B (it can have many) in which some_value does not equal "thisValue"
Thanks.
A Left join is a left outer join. Outer joins preserve one of the tables which is what you are after so good guess.
SELECT *
FROM Table A
LEFT JOIN Table B
ON TableA.Column = TableB.Column
AND B.SomeValue <> 'ThisValue'
All of the rows with a match will have the B information populated all of those without will have nulls in the B data

Factor where clauses into subqueries

I'm wondering if its always possible in SQL to factor a where condition through a join to a subquery. For instance, if I have
select ... from a join b on ... where p and q
and p pertains only to a, q to b, then can I always rewrite as?
select ... from (select ... from a where p) as a join (select ... from b where q) as b on ...
Thanks!
[Notes: 1) I'm using postgres in case this affects the answer. 2) Readability is not an important consideration, as these are automatically generated queries. Edit: 3) I'm not only interested in inner join but other joins as well.]
In general the query 1:
SELECT ...
FROM TableA
JOIN TableB ON <SomeForeignKey>
JOIN TableC ON <SomeForeignKey>
WHERE <SomeConditionOnTableA> AND
<SomeConditionOnTableB> AND
<SomeConditionOnTableC>
... is equivalent to the query 2:
SELECT ...
FROM TableA
JOIN TableB ON <SomeForeignKey> AND <SomeConditionOnTableB>
JOIN TableC ON <SomeForeignKey> AND <SomeConditionOnTableC>
WHERE <SomeConditionOnTableA>
But the same is not true if instead of (INNER) JOINs you use OUTER JOINs. With OUTER JOINs the equivalency holds for very simple conditions that match NOT NULL column values, like:
name='value'
name LIKE '%value%'
number < const
field IN (...)
Notice that these are all conditions that make the OUTER JOINs moot anyway, as they are filtering out rows that have NULL values in the envolved columns... so they would filter out also the rows added by the OUTER JOIN not retrieving anything from the joined table.
But the equivalency breaks if you use OUTER JOINs and start comparing column values with NULLs or comparing expressions that may envolve NULLs.
For example, taking this query (formatted as query 1):
SELECT ...
FROM TableA a
LEFT JOIN TableB b ON <SomeForeignKey>
LEFT JOIN TableC c ON <SomeForeignKey>
WHERE a.somefield = 'whatever'
AND b.name IS NOT NULL
AND c.somenumber >100
In this case the filter is applied after having resolved the OUTER JOIN, and it eliminates both the rows that exist in TableB and have a NULL name, but also removes the rows that where added by the OUTER JOIN not finding a matching row in TableB. This is not equivalent to the query 2 format:
SELECT ...
FROM TableA a
LEFT JOIN TableB b ON <SomeForeignKey> AND b.name IS NOT NULL
LEFT JOIN TableC c ON <SomeForeignKey> AND c.somenumber >100
WHERE a.somefield = 'whatever'
In this case the filter is applied to TableB before resolving the OUTER JOIN. TableB rows that have a NULL name are eliminated by the filter, but reintroduced by the LEFT JOIN. So this query might contain rows that the former does not.
I would say yes, I can't think of a situation where it is not possible. WHERE in it self can be replaced with a join:
select ... from A where x=10
<=>
select ... from A join ( values (10) ) B (x) on A.x = B.x
Perhaps off topic, but for transformations in general Vadim Tropashko (http://arxiv.org/abs/cs/0501053) shows that it is possible to reduce the set of classic relational algebra operators to two binary operations: natural join and generalized union

What means "table A left outer join table B ON TRUE"?

I know conditions are used in table joining. But I met a specific situation and the SQL codes writes like "Table A join table B ON TRUE"
What will happen based on the "ON TRUE" condition? Is that just a total cross join without any condition selection?
Actually, the original expression is like:
Table A LEFT outer join table B on TRUE
Let's say A has m rows and B has n rows. Is there any conflict between "left outer join" and "on true"? Because it seems "on true" results a cross join.
From what I guess, the result will be m*n rows. So, it has no need to write "left outer join", just a "join" will give the same output, right?
Yes. That's the same thing as a CROSS JOIN.
In MySQL, we can omit the [optional] CROSS keyword. We can also omit the ON clause.
The condition in the ON clause is evaluated as a boolean, so we could also jave written something like ON 1=1.
UPDATE:
(The question was edited, to add another question about a LEFT [OUTER] JOIN b which is different than the original construct: a JOIN b)
The "LEFT [OUTER] JOIN" is slightly different, in that rows from the table on the left side will be returned even when there are no matching rows found in the table on the right side.
As noted, a CROSS JOIN between tables a (containing m rows) and table b containing n rows, absent any other predicates, will produce a resultset of m x n rows.
The LEFT [OUTER] JOIN will produce a different resultset in the special case where table b contains 0 rows.
CREATE TABLE a (i INT);
CREATE TABLE b (i INT);
INSERT INTO a VALUES (1),(2),(3);
SELECT a.i, b.i FROM a LEFT JOIN b ON TRUE ;
Note that the LEFT JOIN will returns rows from table a (a total of m rows) even when table b contains 0 rows.
A cross join produces a cartesian product between the two tables, returning all possible combinations of all rows. It has no on clause because you're just joining everything to everything.
Cross join does not combine the rows, if you have 100 rows in each table with 1 to 1 match, you get 10.000 results, Innerjoin will only return 100 rows in the same situation.
These 2 examples will return the same result:
Cross join
select * from table1 cross join table2 where table1.id = table2.fk_id
Inner join
select * from table1 join table2 on table1.id = table2.fk_id
Use the last method
The join syntax's general form:
SELECT *
FROM table_a
JOIN table_b ON condition
The condition is used to tell the database how to match rows from table_a to table_b, and would usually look like table_a.some_id = table_b.some_id.
If you just specify true, you will match every row from table_a with every row of table_b, so if table_a contains n rows and table_b contains m rows the result would have m*n rows.
Most(?) modern databases have a cleaner syntax for this, though:
SELECT *
FROM table_a
CROSS JOIN table_b
The difference between the pure cross join and left join (where the condition is forced to be always true, as when using ON TRUE) is that the result set for the left join will also have rows where the left table's rows appear next to a bunch of NULLs where the right table's columns would have been.

Left Outer join and an additional 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 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.