It is important to understand the logical order of SQL operations when thinking about SQL syntax. JOIN
is an operator (and ON
belongs to the relevant JOIN
) in the FROM
clause. The FROM
clause is the first operation to be executed logically (optimisers can still choose to reorder things).
In your example, there isn't really a difference, but it is easy to construct one, as I've shown in this blog post about the difference between ON
and WHERE
in OUTER JOIN
(the example from the blog post uses the Sakila database):
First query
SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
WHERE fa.film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;
Yields:
ACTOR_ID FIRST_NAME LAST_NAME COUNT
--------------------------------------
194 MERYL ALLEN 1
198 MARY KEITEL 1
30 SANDRA PECK 1
85 MINNIE ZELLWEGER 1
123 JULIANNE DENCH 1
Because we filtered the outer joined table in the WHERE
clause, the LEFT JOIN
was effectively turned into an INNER JOIN
. Why? Because if we had an actor that didn't play in a film, that actor's only row would have fa.film_id IS NULL
, and the fa.film_id < 10
predicate would thus yield NULL
. Such actors are excluded from the result, just as with an INNER JOIN
.
Second query
SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
AND fa.film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;
Yields:
ACTOR_ID FIRST_NAME LAST_NAME COUNT
-----------------------------------------
3 ED CHASE 0
4 JENNIFER DAVIS 0
5 JOHNNY LOLLOBRIGIDA 0
6 BETTE NICHOLSON 0
...
1 PENELOPE GUINESS 1
200 THORA TEMPLE 1
2 NICK WAHLBERG 1
198 MARY KEITEL 1
Now, the actors without films are included in the result, because the fa.film_id < 10
predicate is part of the LEFT JOIN
's ON
predicate
Conclusion
Always place predicates where they make most sense logically.
- Are they part of your
JOIN
operation? Place them in ON
- Are they filters on your entire
JOIN
product? Place them in WHERE