Левое внешнее соединение не сохраняет левую таблицу
У меня есть этот запрос:
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 ответа
Для несоответствующих строк 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
пункт оценивается. Это определенно удалит все строки из предыдущего шага и, возможно, также дополнительные строки.
По сути, вы создали внутреннее соединение, изменение
WHERE FUNCS = 1
в
AND FUNCS = 1
Левое внешнее соединение не может быть указано в предложении WHERE, иначе оно будет отфильтровано
другая проблема - функция LEN для NULL, она не будет больше 0