WHERE followed by ON clause - mysql

What is the semantic difference between the following queries? To me both seemed similar until I executed them on presto. Is this something specific to presto or am I missing something in the SQL standard?
Form 1: All conditions specified in the ON clause.
SELECT
t1.colA,
t1.colB,
t1.colC,
t2.colD
FROM t1
LEFT OUTER JOIN t2
ON t1.colA = t2.colA
AND t1.colB = t2.colB
AND t1.colE = 1
AND t2.colF = 2;
Form 2: Some conditions specified in the WHERE clause instead.
SELECT
t1.colA,
t1.colB,
t1.colC,
t2.colD
FROM t1
LEFT OUTER JOIN t2
ON t1.colA = t2.colA
AND t1.colB = t2.colB
WHERE
t1.colE = 1
AND t2.colF = 2;
Form 1 results in some rows but form 2 doesn't but shouldn't they be equivalent?

There are two important differences.
First the condition t1.colE = 1. A LEFT JOIN keeps all rows in the first table, regardless of what the ON clause evaluates to. So, t1.colE does not change the number of rows in the result set. However, it does have the strange effect that any columns from t2 when this condition is not true are NULL.
Second the condition t2.colF = 2 has a different effect. This turns the LEFT JOIN into an INNER JOIN, because NULL values do not match the WHERE clause.

When you have a LEFT [OUTER] JOIN and WHERE clause that references the right_table, it is equivalent to an [INNER] JOIN. That is, the following two are equivalent:
-- [inner] join
select ...
from left_table
join right_table
on left_table.cola = right_table.cola
and right_table.colb = 2
-- left [outer] join + where
select ...
from left_table
left join right_table
on left_table.cola = right_table.cola
where right_table.colb = 2
Your 2nd example query has a left join and this condition t2.colF = 2 in the where clause, making it different from the first query.
where the the first includes all rows from t1, the second includes only rows where t1.colE = 1 and t2.colF = 2

The way to think about the difference between the WHERE clause and the ON clause is:
The WHERE clause determines which of the rows produced by the FROM clause should be considered in the query, so it's role is to filter rows before they get processed by a GROUP BY, WINDOW or the SELECT clause.
The ON clause determines whether a pair of rows from the left and right side of a JOIN should be considered for the join operation.
For an INNER JOIN, if the ON clause evaluates to false, the rows are not joined with each other and no result is emitted.
For a LEFT JOIN, if, for a given row on the left side the ON clause evaluates to false for all rows on the right side, a row containing the values from the left side and NULLs for the columns on the right side is emitted. The same logic applies for a RIGHT JOIN, but with the sides reversed.
A FULL JOIN is a combination of LEFT and RIGHT joins.
In your example, the second query, any row produced by the LEFT JOIN that contains NULLs in columns from the right side due to the ON clause evaluating to false will be filtered out by the t2.colF = 2 expression WHERE clause.

Related

MySQL - How to get one of the repeated records given a condition in SQL?

I have the next results from a query. I did this:
Where the user "Adriana Smith" with ID 6 is repeated because she has different contract dates, to do that I did a left join from table bo_users to bo_users_contracts (1:m One to Many Relation). The query is below:
SELECT bo_users.ID, bo_users.display_name, COALESCE (bo_users_contracts.contract_start_date,'-') AS contract_start_date, COALESCE (bo_users_contracts.contract_end_date, '-') AS contract_end_date, COALESCE (bo_users_contracts.current,'-') AS current
FROM bo_users
LEFT JOIN bo_users_contracts ON bo_users.ID = bo_users_contracts.bo_users_id
LEFT JOIN bo_usermeta ON bo_users.ID = bo_usermeta.user_id
WHERE (bo_usermeta.meta_key = 'role' AND bo_usermeta.meta_value = 'member')
But I want to get all users, but from user Adriana I just want to get the occurrence where "current" column = 1.
So the final result would be the 3 user's records:
Alejandro, Rhonda and Adriana (with "current" = 1)
Thank you!
Since you want to limit on a table being outer joined, the limit should be placed on the join itself so the all records from bo_users is retained. (as indicated desired by the outer join)
Essentially the limit is applied before the join so the unmatched records from BO_users to bo_users_contracts are kept. If applied after the join in a where clause the records from BO_user without a matching record would have a null value for current and thus be excluded when the current=1 filter is applied.
In this example the only values which should be in the where would be from table BO_USERS.
I'd even move the bo_usermeta filters to the join or you may lose bo_users; or the left join on the 3rd table should be an inner join.
SELECT bo_users.ID
, bo_users.display_name
, COALESCE (bo_users_contracts.contract_start_date,'-') AS contract_start_date
, COALESCE (bo_users_contracts.contract_end_date, '-') AS contract_end_date
, COALESCE (bo_users_contracts.current,'-') AS current
FROM bo_users
LEFT JOIN bo_users_contracts
ON bo_users.ID = bo_users_contracts.bo_users_id
and bo_users_contracts.current = 1
LEFT JOIN bo_usermeta --This is suspect
ON bo_users.ID = bo_usermeta.user_id
WHERE (bo_usermeta.meta_key = 'role' --this is suspect
AND bo_usermeta.meta_value = 'member') --this is suspect
The lines reading this is suspect are that way because you have a left join which means you want all users from bo_users.. However if a user doesn't have a meta_key or meta_value defined, they would be eliminated. Either change the join to an inner join or move the where clause limits to the join. I indicate this as you're query is "inconsistent" in it's definition leading to ambiguity when later maintained.

How to join two tables using left join with multiple conditions?

I want to join two tables using left join on multiple conditions.
I have two STS_CD flags AC or IA.
I want to get data where STS_CD='AC' but i got it on both AC or IA.
My Query is:
SELECT `bldr`.`id` AS `bldr_ID`,
`bldr`.`sts_cd` AS `bldr_STS_CD`,
bldr.shrt_nm
FROM `bldr`
LEFT JOIN `bldr_img`
ON `bldr`.`id` = `bldr_img`.`prnt_id`
AND `bldr_img`.`img_cat_cd` = 'LG'
AND `bldr`.`sts_cd` = 'AC'
AND `bldr_img`.`sts_cd` = 'AC'
ORDER BY `bldr`.`id`
How do I do this?
This is your query:
SELECT bldr.ID as bldr_ID, bldr.STS_CD AS bldr_STS_CD,bldr.SHRT_NM
FROM bldr LEFT JOIN
bldr_img
ON bldr.ID = bldr_img.PRNT_ID AND
bldr_img.IMG_CAT_CD = 'LG' AND
bldr.STS_CD = 'AC' AND
bldr_img.STS_CD = 'AC'
ORDER BY bldr.ID;
When using LEFT JOIN, conditions on the second table should go in the ON clause. Conditions on the first table should be in the WHERE (except for the JOIN condition, of course). So try this:
SELECT bldr.ID as bldr_ID, bldr.STS_CD AS bldr_STS_CD,bldr.SHRT_NM
FROM bldr LEFT JOIN
bldr_img
ON bldr.ID = bldr_img.PRNT_ID AND
bldr_img.IMG_CAT_CD = 'LG' AND
bldr_img.STS_CD = 'AC'
WHERE bldr.STS_CD = 'AC'
ORDER BY bldr.ID;
Why? Although this seems arcane it makes perfect sense. A LEFT JOIN keeps all rows in the first tables regardless of whether the ON clause evaluates to true, false, or even NULL. It doesn't "know" whether the condition is on the first table or the second. Hence, filters on the first table are not effective.

Mysql join not getting proper values

I have 3 tables
movies_detais
movies_revies
movies_gossips
What I want is that I want all the data whose movies_relesed_type=0 and movies_type=1
But I am not getting desired values
Code
Select md.movies_name,
md.movies_description,
mr.rt_user_comments,
mg.gossip_desc
from movies_details md
Inner join movies_reviews mr
on md.movies_id=mr.movie_review_id
Inner join movies_gossips mg
on md.movies_id=mg.movies_gossip_id
and md.movie_relesed_type='0'
and md.movie_type='1'
With this code I am only getting one row whose movies_relesed_type=0 and movies_type=1, but in my table I am having other rows also which meets the condition but they are not displaying.
I think this is a case where you want the conditions in the where clause:
Select md.movies_name,
md.movies_description,
mr.rt_user_comments,
mg.gossip_desc
from movies_details md Inner join
movies_reviews mr
on md.movies_id=mr.movie_review_id left join
movies_gossips mg
on md.movies_id = mg.movies_gossip_id
where md.movie_relesed_type='0' and md.movie_type = '1';
A left outer join returns all the rows from the first table, even when the condition in the on clause evaluates to not-true. This is true regardless of which table the conditions refer to. So, you cannot filter on the first table in the on clause using a left outer join.

retrieve only records that has join records

I have following sql query, which retrieves me joined records. However I want to get places which doesnt have joined records, see below:
SELECT `places`.* FROM `places` INNER JOIN `quotes` ON `places`.`id` = `quotes`.`place_id`
WHERE `quotes`.`user_id` = 1 AND
Here comes part I don't know how to write, but I want only to get places where count of quotes = 0 like:
"quotes.count == 0"
How to add another clause to this sql query, to match my requests?
You want an outer join, presumably a left outer join:
SELECT `places`.*
FROM `places` LEFT JOIN
`quotes`
ON `places`.`id` = `quotes`.`place_id` AND
`quotes`.`user_id` = 1
WHERE quotes.place_id is null;
Note that the condition in the where clause has been moved to the on clause. When there is no match, the columns from quotes are filled with NULLs -- which would cause a where condition to fail.

How to join three tables in Oracle where one of the table has null values in the foreign key column?

I have to execute the following:
select items.segment1
from items
,po_lines
,po_shipments
where po_lines.item_id = items.item_id(+) --po_lines has null in some item_id
po_shipments.ship_to = items.org_id;
But the outer join seem to be not working. The query should return 100 values while it is returning only 85. It is neglecting the 15 for which po_lines.item_id is null.
Any help would be appreciated.
The (+) operator should be applied to the table that's allowed to have "missing" rows - you have it on the wrong side of the join.
select items.segment1
from items
,po_lines
,po_shipments
where po_lines.item_id (+) = items.item_id --po_lines has null in some item_id
po_shipments.ship_to = items.org_id;
Quoting Wikipedia JOIN (SQL):
ANSI-standard SQL specifies five types of JOIN: INNER, LEFT OUTER,
RIGHT OUTER, FULL OUTER and CROSS
You can use left join in this way:
select items.segment1
from
items
left outer join
po_lines on po_lines.item_id = items.item_i
inner join
po_shipments on po_shipments.ship_to = items.org_id;
Your oracle (*) syntax is deprecated:
Starting with Oracle9i, the confusing outer join syntax using the
‘(+)’ notation has been superseded by ISO 99 outer join syntax.