SQL JOIN - WHERE clause vs. ON clause

前端 未结 19 1605
深忆病人
深忆病人 2020-11-21 11:56

After reading it, this is not a duplicate of Explicit vs Implicit SQL Joins. The answer may be related (or even the same) but the question is diffe

19条回答
  •  一生所求
    2020-11-21 12:35

    I think this distinction can best be explained via the logical order of operations in SQL, which is, simplified:

    • FROM (including joins)
    • WHERE
    • GROUP BY
    • Aggregations
    • HAVING
    • WINDOW
    • SELECT
    • DISTINCT
    • UNION, INTERSECT, EXCEPT
    • ORDER BY
    • OFFSET
    • FETCH

    Joins are not a clause of the select statement, but an operator inside of FROM. As such, all ON clauses belonging to the corresponding JOIN operator have "already happened" logically by the time logical processing reaches the WHERE clause. This means that in the case of a LEFT JOIN, for example, the outer join's semantics has already happend by the time the WHERE clause is applied.

    I've explained the following example more in depth in this blog post. When running this 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 film_id < 10
    GROUP BY a.actor_id, a.first_name, a.last_name
    ORDER BY count(fa.film_id) ASC;
    

    The LEFT JOIN doesn't really have any useful effect, because even if an actor did not play in a film, the actor will be filtered, as its FILM_ID will be NULL and the WHERE clause will filter such a row. The result is something like:

    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
    

    I.e. just as if we inner joined the two tables. If we move the filter predicate in the ON clause, it now becomes a criteria for the outer join:

    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 film_id < 10
    GROUP BY a.actor_id, a.first_name, a.last_name
    ORDER BY count(fa.film_id) ASC;
    

    Meaning the result will contain actors without any films, or without any films with FILM_ID < 10

    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
    

    In short

    Always put your predicate where it makes most sense, logically.

提交回复
热议问题