Шрифт:
Интервал:
Закладка:
Полное внешнее соединение (FULL OUTER JOIN) является целиком включающим. Оно возвращает одну строку для каждой пары соответствующих потоков и одну частично заполненную строку для каждой строки из каждого потока, где соответствие не было найдено. Данное соединение объединяет поведение правого и левого соединений.
Рис. 22.4. Полное соединение
Вот оператор, использующий те же входные потоки, что и наш пример INNER JOIN:
SELECT
Table1.PK,
Table1.COL1,
Table2.PKX,
Table2.COLX
FROM Table1 FULL JOIN Table2
ON Table1.PK = Table2.FK
WHERE ... <условия-поиска>
На рис. 22.4 показано, как будут объединены потоки для полного соединения.
Перекрестные соединения
Firebird не поддерживает языковых элементов для перекрестного соединения (CROSS JOIN), которое создает набор данных, являющийся декартовым произведением двух таблиц. То есть для каждой строки левого потока выходной поток будет содержать каждую строку из правого потока. В редких случаях, когда вам нужно декартово произведение, вы можете использовать синтаксис SQL-89 без каких-либо критериев соединения в предложении WHERE, например:
SELECT T1.*, Т2.* FROM T1 TABLE1, T2 TABLE2;
Обработчик запросов Firebird иногда внутренне создает перекрестные соединения, когда конструирует "реки" в процессе соединения операций (см. разд. "Темы оптимизации" этой главы).
Естественные соединения
Firebird не поддерживает естественное соединение (NATURAL JOIN), также известное как эквисоединение (EQUI JOIN), которое создает набор данных, связывая два потока на основе соответствия столбцов, использующих общие идентификаторы столбцов с одинаковыми значениями. В Firebird вы всегда должны указывать условия соединения.
Двусмысленность в запросах JOIN
В различных книгах по теории баз данных сказано, что двусмысленность может существовать, только когда некоторые имена столбцов появляются в нескольких потоках. Человек, практически работающий с базами данных, может рассказать другую историю. Вопрос устранения двусмысленности отпадает в зависимости от способа, каким сервер базы данных реализует синтаксический разбор, создание потоков и сортировку, которые выполняются в процессе операции соединения.
InterBase был снисходителен к отклонениям от недвусмысленности в синтаксисе соединений - иногда с неудачными результатами. Если вы переносите существующий код вашего приложения, работавшего с InterBase, не пугайтесь исключений SQL, которые выдаст Firebird в процессе первого тестового выполнения запросов с соединениями. Он покажет вам места в вашем коде, где раньше допускалось выполнение запросов, которые могли возвращать неверные результаты.
Firebird не примет запросы соединения, содержащие ссылки на столбцы, которые не имеют полного квалификатора таблицы. Квалификатор таблицы может быть идентификатором таблицы или алиасом таблицы. Начиная с версии 1.5 недопустимо их смешивать. Если вы используете версию 1.0, будьте последовательны, если хотите избежать переписывания кода при переходе на новые версии.
Предыдущие примеры использовали идентификаторы таблиц. Использование алиасов таблиц более элегантно и компактно, а для некоторых запросов (см. разд. "Реентерабельные соединения") является обязательным.
Алиасы таблиц
Если имя таблицы длинное или запутанное, или если существует много таблиц, использование алиасов таблиц будет полезным (и в некоторых случаях необходимым) для большей ясности операторов. Обработчик запроса трактует алиас таблицы как синоним таблицы, которую алиас представляет. Алиасы таблиц обязательны в запросах, которые формируют множество потоков из одной и той же таблицы.
! ! !
СОВЕТ. В базе данных диалекта 3, которая была создана с использованием ограниченных идентификаторов (delimited identifiers), комбинированных с символами в нижнем регистре или при смешивании регистров в именовании объектов и/или с использованием "неправильных символов", написание запросов может стать довольно утомительным занятием, часто порождающим ошибки, если не используются алиасы.
. ! .
Алиас может быть использован везде, где он допустим для обозначения имени таблицы, как квалификатор; имена всех таблиц должны быть заменены на алиасы. Смешивание идентификаторов таблиц с алиасами в одном и том же запросе приведет к исключениям в Firebird 1.5 и следующих версиях.
Следующий пример использует идентификаторы таблиц:
SELECT
TABLEA.COL1,
TABLEA.COL2,
TABLEB.COLB,
TABLEB.COLC,
TABLEC.ACOL
FROM TABLEA
JOIN TABLEB ON TABLEA.COL1 = TABLEB.COLA
JOIN TABLEC ON TABLEB.COLX = TABLEC.BCOL
WHERE TABLEA.STATUS = 'SOLD'
AND TABLEC.SALESMAN_ID = '44'
ORDER BY TABLEA.COL1;
Тот же пример, использующий алиасы:
SELECT
A.COL1,
A.COL2,
B.COLB,
B.COLC,
C.ACOL
FROM TABLEA А /* задает алиас */
JOIN TABLEB В ON A.COL1 = В.COLA
JOIN TABLEC С ON В.COLX = C.BCOL
WHERE A.STATUS = 'SOLD'
AND С.SALESMAN_ID = '44'
ORDER BY A.COL1;
Допустимые имена алиасов таблиц
Используйте любую строку, содержащую до 31 символа, которые допустимы для имен метаданных (т. е. алфавитно-цифровые символы в кодировке ASCII в диапазонах 35-38, 48-57, 64-90 и 97-122). Пробелы, диакритические знаки, имена, заключенные в кавычки, и имена, начинающиеся с цифры, недопустимы.
Внутренний курсор
При чтении в потоке сервер базы данных использует указатель, чей адрес изменяется при продвижении чтения от начала к концу. Этот указатель называется курсором - не путайте с курсором набора, который создается в SQL с помощью DECLARE CURSOR. Внутренние курсоры доступны клиентам только посредством синтаксиса соединений и подзапросов.
Рис. 22.5. Внутренний курсор для соединения двух таблиц
Текущим адресом внутреннего курсора является абсолютное смещение от адреса первого потока в наборе, что означает, что он может продвигаться только вперед. Внутри оптимизатор использует индексы и организует входные потоки в план, чтобы гарантировать, что запрос начинает возвращать выходные данные в самое короткое по возможности время. Процесс оптимизации и планы детально обсуждаются в разд. "Темы оптимизации" в конце этой главы.
В любых многотабличных операциях сервер Firebird поддерживает один внутренний курсор для каждого потока. В предыдущих примерах соединений таблиц TableA и TableB каждая имеет свой курсор. Когда появляется соответствие, сервер создает объединенный образ для выходного потока, копируя потоки с текущего адреса из двух курсоров, как показано на рис. 22.5.
Реентерабельные соединения
Условия проектирования иногда требуют формирования объединенного набора из двух или более потоков, которые поступают из одной и той же таблицы. Часто таблица проектируется с иерархической, или древовидной, структурой, где каждая строка в таблице логически является потомком родителя в той же таблице. Первичный ключ таблицы указывает на узел дерева уровня потомка, в то время как колонка внешнего ключа хранит родительский ключ, ссылающийся на значение первичного ключа в другой строке.
Запрос на "выравнивание" отношения родитель-потомок требует соединения, которое извлекает один поток для родителей, а другой - для потомков из той же таблицы. Обычный термин для этого - ссылающееся на себя соединение. Термин реентерабельное соединение морфологически является более подходящим, т. к. существуют другие типы реентерабельных запросов, которые не используют соединений. Позже в этой главе в разд. "Подзапросы" обсуждаются другие типы реентерабельных запросов.
Курсоры для реентерабельных соединений
Для выполнения реентерабельного соединения сервер поддерживает один внутренний курсор для каждого потока в пределах образа одной таблицы. Концептуально он трактует контексты двух курсоров, как если бы они были отдельными таблицами. В этой ситуации синтаксис ссылок на таблицу обязательно использует алиасы для различения курсоров двух потоков.
В следующем примере каждое подразделение организации хранится с родительским идентификатором, который указывает на первичный ключ его руководящего подразделения. Приведенный далее запрос трактует подразделения и родительские подразделения, как если бы они были двумя таблицами:
SELECT
D1.XD,
D1.PARENTID,
D1.DESCRIPTION AS DEPARTMENT,
D2.DESCRIPTION AS PARENT_DEPT
FROM DEPARTMENT D1
LEFT JOIN DEPARTMENT D2
/* левое соединение обращается к корню дерева, ID 100 */
ON Dl.PARENTID = D2.ID;
На рис. 22.6 проиллюстрировано, как выбираются записи потомков в реентерабельном обращении к таблице DEPARTMENT.
Рис. 22.6. Внутренние курсоры для простого реентерабельного соединения Простой двухуровневый результат показан на рис. 22.7.
Результат этого запроса очень простой - одноуровневая денормализация. Операции получения древовидного результата для таблиц такого вида часто являются рекурсивными, они используют хранимые процедуры для реализации и управления рекурсией[79].
- Delphi. Учимся на примерах - Сергей Парижский - Программирование
- Сделай видеоигру один и не свихнись - Слава Грис - Программирование / Руководства
- Психбольница в руках пациентов - Алан Купер - Программирование