Шрифт:
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] .
Рис. 22.7. Результат простого реентерабельного соединения
Подзапросы
Подзапрос - это специальный вид выражения, которое фактически является запросом SELECT к другой таблице, включенным в спецификацию основного запроса. Выражение включенного запроса называют подзапросом, вложенным запросом, встроенным запросом, а иногда (ошибочно) Sub-SELECT.
79
В "SQL for Smarties" Joe Celco предлагает интересные решения для хранения и поддержки древовидных структур в реляционных базах данных (Morgan Kaufmann, 1999).
В Firebird версии 1.5 и выше выражения подзапроса используются тремя способами:
* для получения одной строки или многострочного входного набора для операции INSERT (его синтаксис описан в разд. "Оператор INSERT" главы 20);
* для задания в процессе выполнения выходного столбца только для чтения для запроса SELECT;
* для получения значений или условий для предикатов поиска.
В версиях Firebird после 1.5 появляется четвертый вариант подзапросов- виртуальная таблица, который кратко обсуждается в главе 24.