Левое внешнее соединение не сохраняет левую таблицу - PullRequest
4 голосов
/ 10 сентября 2011

У меня есть этот запрос:

SELECT Q_ID,
   Q_DESC,
   COUNT(Q_ID)
FROM   #tmp_rep
   LEFT OUTER JOIN po_Questions po
        ON  Q_ID = Certificate AND FUNCS = 1
        AND LEN(LTRIM(RTRIM(po.UserName))) > 0
        AND LEN(LTRIM(RTRIM(po.UserNumber))) > 0
GROUP BY
   Q_ID,
   Q_DESC
ORDER BY
   Q_ID

таблица #tmp_rep имеет 2 столбца (Q_ID, Q_Desc) и 4 строки. И таблица po_Questions имеет 10, которые используют 3 кода Q_ID вСертификат столбца строк.Если я выполню этот запрос, все будет в порядке, и для Q-ID = 4 я получу 0 для подсчета, но если я напишу этот запрос следующим образом:

SELECT Q_ID,
   Q_DESC,
   COUNT(Q_ID)
FROM   #tmp_rep
   LEFT OUTER JOIN po_Questions po
        ON  Q_ID = Certificate 
WHERE FUNCS = 1
      AND LEN(LTRIM(RTRIM(po.UserName))) > 0
        AND LEN(LTRIM(RTRIM(po.UserNumber))) > 0

GROUP BY
   Q_ID,
   Q_DESC
ORDER BY
   Q_ID

, тогда я получу только 3 строки в результате иQ_ID = 4 не принадлежит результату. Почему SQL Server имеет такое поведение?

спасибо

Ответы [ 2 ]

6 голосов
/ 10 сентября 2011

Для несоответствующих строк po.UserName будет NULL, поэтому LEN(LTRIM(RTRIM(po.UserName))) равно NULL

NULL > 0 оценивается как UNKNOWN, а не TRUE, поэтому, когда предикат находится в WHERE Вы превращаете свое внешнее соединение обратно во внутреннее.Аналогично для FUNCS, как указывает SQLMenace.

Возможно, вы захотите скачать плакат Ицик Бен Гана по обработке логических запросов .

Концептуально происходит следующее (это не следует путать с тем, как оно физически реализовано, однако!)

Для вашего первого запроса:

  • Декартово произведение на #tmp_rep, po_Questions
  • Затем применяется ON Filter, который эффективно делает INNER JOIN на Q_ID = Certificate, но также исключает любые po_Questions строки, которыене соответствует вашему предикату.
  • Затем несоответствующие Outer строки из #tmp_rep будут добавлены обратно. Они будут иметь NULL для всех столбцов из po_Questions
  • Тамне является условием WHERE, так что это конечный результат.

Для вашего второго запроса:

  • Декартово произведение на #tmp_rep, po_Questions
  • Затем применяется ON Filter, который фактически делает INNER JOIN на Q_ID = Certificate.
  • Затем затем возвращаются несоответствующие Outer строки из #tmp_rep. Они будут иметь NULLдля всех столбцов от po_Questions
  • Затем оценивается предложение WHERE.Это определенно удалит все строки из предыдущего шага и, возможно, дополнительные строки.
2 голосов
/ 10 сентября 2011

По сути вы создали внутреннее соединение, измените

WHERE FUNCS = 1

до

AND FUNCS = 1

Левое внешнее соединение не может быть указано в предложении WHERE, иначе оно будет отфильтровано

другая проблема - функция LEN для NULL, она не будет больше 0

...