В чем разница между On и Где в LEFT JOIN? - PullRequest
4 голосов
/ 27 февраля 2012

В чем разница между следующими частями кода SQL:

select count(*)
from A
left join B
on a.id = b.id
where a.status = 2 and
b.id is NULL

и

select count(*)
from A
left join B
on a.id = b.id
and a.status =2
where b.id is NULL

? Я прочитал это: Семантическая разница между запросами на соединение , но я до сих пор не знаю, какой из них лучше использовать

Я провел последний час, читая это и понимая все ответы, добавив ценность, но я понял, что пример кода вместе с «никогда не сталкивался с этим» лучшим

Ответы [ 4 ]

10 голосов
/ 27 февраля 2012

Это ключ к тому, как используются соединения LEFT, один отфильтрует результат, другой просто потерпит неудачу при LEFT соединении, сохранив данные со стороны LEFT в JOIN.

(1) левое соединение B на a.id = b.id где a.status = 2

Игнорируя другие фильтры, это говорит LEFT JOIN к таблице B, поэтому «попробуйте присоединиться к таблице B, используя условие a.id=b.id».
Если вы не можете сделать совпадение, сохраните записи в левой таблице (т.е. A). После этого в оставшихся записях FILTER out (т.е. УДАЛИТЬ) записи, которые не совпадают a.status=2

(2) осталось присоединиться к B on a.id = b.id и a.status = 2

Игнорируя другие фильтры, это говорит LEFT JOIN против таблицы B при 2 условиях, поэтому «попробуйте присоединиться к таблице B при обоих условиях a.id = b.id and a.status =2». Если вы не получите записей от B при обоих условиях (даже если один не связан с B), сохраните запись из A в любом случае.

5 голосов
/ 27 февраля 2012

Перезапись 2 запросов без LEFT JOIN / IS NULL, поэтому очевидно, что они могут возвращать разные наборы данных в определенных случаях:

---Query 1

SELECT COUNT(*)
FROM a
WHERE a.status = 2
  AND NOT EXISTS 
      ( SELECT *
        FROM b
        WHERE a.id = b.id
      )

и

---Query 2

SELECT COUNT(*)
FROM a
WHERE NOT ( a.status = 2
        AND EXISTS 
            ( SELECT *
              FROM b
              WHERE a.id = b.id
            )
          )

--- or:

---Query 2
SELECT COUNT(*)
FROM a
WHERE a.status <> 2
   OR NOT EXISTS 
      ( SELECT *
        FROM b
        WHERE a.id = b.id
      )
5 голосов
/ 27 февраля 2012

Разница заключается в том, где условие логически оценивается, что, в свою очередь, может повлиять на набор результатов.

В ваших примерах (переформатированных) у вас есть:

Пример 1

SELECT COUNT(*)
  FROM A LEFT JOIN B ON a.id = b.id
 WHERE a.status = 2 AND b.id is NULL

Пример 2

SELECT COUNT(*)
  FROM A LEFT JOIN B ON a.id = b.id AND a.status = 2
 WHERE b.id is NULL

В первом случае применяется LEFT JOIN и генерируется результирующий набор; затем это фильтруется двумя условиями в предложении WHERE.

Во втором случае LEFT JOIN формируется с условием фильтра на a.status, и в некоторых случаях может изменить набор результатов из LEFT JOIN. Этот набор результатов затем фильтруется с помощью основного предложения WHERE.

Пример 2 по существу эквивалентен:

Пример 2А

SELECT COUNT(*)
  FROM (SELECT * FROM A WHERE a.status = 2) AS A
  LEFT JOIN B ON a.id = b.id
 WHERE b.id is NULL

Для некоторых запросов (но, вероятно, не для этого) разница может иметь значение.


Давайте попробуем создать несколько простых примеров данных:

Table A               Table B
id    status          id
4     2               1
5     3

Пример 1 будет иметь промежуточный набор результатов:

a.id   a.status   b.id
4      2          null
5      3          null

и предложение WHERE исключает второй ряд.

Пример 2 будет иметь промежуточный набор результатов:

a.id   a.status   b.id
4      2          null

В этом примере чистый результат такой же, и я не смог найти данные, которые не совпадают.

Если условие запроса, которое перемещается, находится во внешней объединенной таблице и является более сложным, чем простое равенство, то вы можете увидеть эффект.

1 голос
/ 28 февраля 2012

Возможно, за этим проще следовать:

SELECT id
  FROM A
 WHERE status = 2
EXCEPT
SELECT id
  FROM B;

В маловероятном случае вас интересует только количество результатов:

SELECT COUNT(*)
  FROM ( SELECT id
           FROM A
          WHERE status = 2
         EXCEPT
         SELECT id
           FROM B ) AS DT1;
...