|
Программирование >> Полное сканирование таблицы
ния, независимо от содержимого присоединяемой внешне таблицы. Необычные внешние соединения, которые я опишу в следующих разделах, подразумевают некоторые противоречия самой причине использования внешнего соединения. Фильтрованные внешние соединения Рассмотрим рис. 7.23, где внешнее соединение производится с таблицей с фильтрующим условием. Во внешнем случае, то есть случае, когда для строки Т1 нет подходящей строки из 12, база данных назначает в результирующей строке каждому столбцу 12 значение null. Таким образом, кроме 12. Некоторый столбец IS NULL, практически любое фильтрующее условие для 12 приведет к исключению результирующей строки, которая создается во внешнем случае внешнего соединения. Рис. 7.23. Внешнее соединение с фильтрованным узлом Даже такие условия, какT2.Unpaid Flад != Y илиN0TT2.Unpaid Fag= Y, которые, как вы ожидаете, должны быть истинными во внешнем случае, на самом деле истинными не являются. ВНИМАНИЕ Когда дело доходит до условий в разделе WHERE, базы данных интерпретируют значения null не интуитивным образом. Если вы считаете, что null по отношению к столбцу таблицы обозначает неизвестно , а не не применимо , то, возможно, сможете понять, itait базы данных обрабатывают значения null в условиях. Кроме вопросов о том, равно ли значение в столбце null, практически любой вопрос, который вы можете задать о неизвестном значении, вернет ответ неизвестно , что, на самом деле, и есть истинностное значение для большинства условий по null. С точки зрения отбрасывания строк для запроса, база данных обрабатывает истинностное значение неизвестно itaK FALSE, отбрасывая строки с неизвестными истинностными значениями во фразе WHERE. И, хотя NOT FALSE = TRUE, вы обнаружите, что NOT неизвестно = неизвестно ! Так как большинство фильтров для внешней таблицы отбрасывают внешние случаи внешних соединений, и предназначение внешних соединений состоит в сохранении таких случаев, вам необходимо обращать особое внимание на любые фильтры для внешних таблиц. Существует несколько сценариев, и вам следует потратить достаточно времени, чтобы определить, какой применим для конкретного случая. Фильтр достаточно редкий, например Некоторый столбец IS NULL, и может вернуть значение TRUE для полей, равных null, принудительно вставленных во внешнем случае, и поэтому фильтр функционально верен. УОЮВИЯ BHElUHErO СОЕДИНЕНИЯ ДЛЯ ОДНОЙ ТАБЛИЦЫ В старом стиле записи Oracle превращение фильтрующего условия в часть соединения производится за счет добавления (+). Например, двусоставное внешнее соединение с таблицей Cocie Translations, которое использовалось в предыдущих примерах, выглядело бы так: WHERE ... AND 0.Order Type Code - OTypeTrans.Code(+) AND OTypeTrans.Type(+) - ORDERJYPE В новом стиле записи соединений ANSI, едииствеином разрешенном в DB2 для записи внешних соединений, фильтрующее условие переходит в раздел FROM, чтобы стать явным условием соединения: FROM ... Orders О ... LEFT OUTER JOIN Codejranslations OTypeTrans ON 0.Order Type Code - OTypeTrans.Code AND OTypeTrans.Type - ORDER TVPE В первоначальной записи внешних соединений в SQL Server база данных просто предполагает, что фильтрующее условие является частью соединения: WHERE ... AND 0.Order Type Code *= OTypeTrans.Code AND OTypeTrans.Type = ORDERJYPE Обратите внимание, что это делает рассмотренную проблему невозможной для внешних соединений в SQL Server в старом стиле; база данных автоматически делает фильтр частью соединения. Также обратите внимание, что в редких случаях, когда фильтр действительно является фильтром, для получения желаемого результата вам необходимо либо преобразовать запись в новый стиль, либо превратить соединение в эквивалентный запрос NOT EX ISTS, о чем я расскажу позже. Разработчик не планировал отбрасывать внешнее соединение, и фильтрующее условие необходимо удалить. Фильтрующее условие предназначено для отбрасывания внещнего соединения, и соединение с таким же успехом может быть внутренним. В этом случае нет никакого функционального различия между запросом с соединением, выраженным как внешнее или внутреннее соединение. Однако, сделав его формально внутренним соединением, вы дадите базе данных больше степеней свободы для создания планов исполнения, которые могут выполнять соединение в любом направлении. Когда наилучший фильтр находится на той же стороне соединения, где и ранее присоединяемая внешне таблица, дополнительные степени свободы могут позволить выбрать лучший план исполнения. С другой стороны, превращение соединения во внутреннее может заставить оптимизатор сделать ошибку, которой он бы избежал с внешним соединением. Внешние соединения - это один из способов ограничения порядка соединения, когда вы сознательно хотите сделать это, даже если вам совсем не требуется сохранять внещний случай. Фильтрующее условие добавлено намеренно, но оно должно быть частью соединения! Если фильтрующее условие сделать частью соединения, то запрос к базе данных будет звучать как Для каждой строки детальной таблицы найти подходящую строку из этой таблицы, удовлетворяющую фильтру, если тшсо-вая существует; иначе, поставить в соответствие псевдостроку, состоящую из значений null*. Рассмотрим первый сценарий подробнее. Возьмем запрос следующего вида: SELECT ... FROM Employees Е LEFT OUTER JOIN Departments D ON E.Department ID = D.Department 10 WHERE D.Dept Manager ID IS NULL Что же на самом деле запрос хочет получить от базы данных? Семантически он запрашивает два различных набора строк. Один набор содержит данные обо всех сотрудниках, для которых не указан отдел, а второй отыскивает всех сотрудников, для которых указан отдел, не имеющий начальника. Существует вероятность, что приложение действительно хочет одновременно получить два таких разных набора, но более вероятно, что разработчик не заметил, что у такого простого запроса настолько сложный результат, и ему не нужен один из этих наборов строк. Рассмотрим слегка отличающийся пример: SELECT ... FROM Employees Е LEFT OUTER JOIN Departments D ON E.DepartmentJD = D.Department ID WHERE D.Department ID IS NULL С первого взгляда кажется, что это неестественный запрос, потому что первичный ключ (Department ID) таблицы Departments не может быть равен null. Даже если бы null было допустимым значением первичного ключа, подобное соединение с другой таблицей никогда бы не было успешным для такого ключевого значения (так как условное выражение NULL = NULL возвращает истинностное значение неизвестно ). Однако, так как это внешнее соединение, существует разумная интерпретация этого запроса - Найти всех сотрудников, для которых не существует соответствующих отделов . Во внешнем случае этих внешних соединений все столбцы Departments, включая даже обязательно не равные null столбцы, получат значение null, поэтому условие D. Department ID IS NULL будет истинно только во внешнем случае. Есть и более распространенный и простой для восприятия способ записи этого запроса: SELECT .. FROM Employees Е WHERE NOT EXISTS (SELECT * FROM Departments D WHERE E.DepartmentJD = D.Department ID) Хотя форма NOT EXISTS запросов такого рода более естественна и ее проще читать и понимать, в настройке SQL есть место и для другой формы (для лучшего понимания ее рекомендуется хорошо комментировать). Преимущество выражения условия NOT EXISTS в виде внешнего соединения, за которым следует Первич-ный ключ IS NULL, заключается в том, что такая форма позволяет более точно контролировать, когда именно в плане исполнения будет выполнено соединение, и когда будет использована селективность этого условия. Обычно условия NOT EXISTS оцениваются после всех обычных соединений, по крайней мере, в Oracle. Это один из примеров, когда фильтр (не являющийся частью внешнего соединения) на присоединяемой внешне таблице действительно может быть предумышленным и верным.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |