Россия |
Язык SQL
8.13 Шаблоны запросов языка SQL и сложные запросы
Запросы SQL можно представлять построенными в шаблонах, создаваемых на некотором наборе основных шаблонов. Сразу оговоримся, что эти шаблоны никакого отношения к регулярным выражениям не имеют. Конечно, вводимое представление не отменяет и не заменяет синтаксис языка. Речь идёт о восприятии запросов человеком.
8.13.1 Структура шаблонов SQL
В предыдущих разделах мы изучали синтаксис для некоторых типов запросов. Обратим внимание на то, что каждая такая запись представляет шаблон, состоящий из служебных слов, предусмотренных заранее и записанных в определённом порядке, и заполняемых полей. Мы говорили о том, что запрос представляет набор фраз.
Так, самый сложный запрос, который можно записать в рамках исчисления на кортежах без использования вложенных подзапросов
SELECT DISTINCT {* |{столбец|константа [псевдоним]}, ... } FROM {таблица, ... } WHERE условие(я)
состоит из трёх расположенных последовательно фраз, помеченных метками (лейблами) SELECT DISTINCT, FROM и WHERE. За каждой меткой следует поле, предназначенное для ввода информации пользователя, своей для каждого конкретного запроса.
Обратите внимание на то, что для транслятора SQL любая инструкция это строка, а для человека удобнее двумерное графическое представление, в котором фразы как-то выделены и структурированы.
В общем случае шаблоны любых инструкций языка представляют собой чередование меток, представленных текстовыми константами, и связанных с метками переменных составляющих, представленных на рисунках ниже в виде полей. При реализации запроса по шаблону в поля заполнения, в соответствии с правилами работы с шаблоном, помещают либо фактические значения, либо ссылки на другие шаблоны, либо сами эти шаблоны. Это позволяет конструировать сложные шаблоны из небольшого набора базисных конструкций. Конечно, для каждого поля существуют свои правила заполнения и не все комбинации шаблонов допустимы. Более того, при подключении шаблона к другому шаблону не исключена возможность появления ранее не существовавших ограничений.
Выделим два типа шаблонов —простой и рекурсивный (рисунок 8.7). Простой шаблон состоит из текстовых полей меток, обозначенных на рисунке буквой "л", и полей заполнения, помеченных "п". В рекурсивном шаблоне часть его конструкции может быть повторена. Рекурсивное употребление самого шаблона задается правилами сочетания шаблонов.
Поясним представление рекурсивного шаблона. В SQL такие шаблоны не могут быть основными. Они определяют структуры фраз основного шаблона. Непустой начальный шаблон необходим, так как пустого заполнения поля быть не должно. Результирующий шаблон строится на основе начального шаблона. При этом точка входа для пополнения следующим элементом не обязательно лежит в голове образующейся структуры. Возможно встраивание в середину.
Шаблоны можно представлять как классы запросов, которые в них могут быть построены.
8.13.2 Классификация запросов
Чего можно добиться, представляя сложные запросы с помощью структурированных шаблонов? Во-первых, создание классификации запросов. С помощью графического представления сделаем её удобной для восприятия человеком и потому обозримой. Во-вторых, и это главное, построим систему правил, позволяющую быстро писать любые запросы. Заметьте, я не говорю об алгоритме написания запросов, потому, что многие из предлагаемых правил трудно формализуемы, содержат исключения и неопределённости, а пути решения могут выбираться неоднозначно. Тем не менее, они позволяют навести некоторый порядок.
Выделим три основных класса запросов (рисунок 8.8):
- запросы без подзапросов;
- запросы с подзапросами:
- запросы, учитывающие вложенные структуры.
Сразу уточняем приведённые понятия. Прежде всего, "запрос без подзапросов" включает в себя три категории:
- одиночный запрос к одной таблице;
- одиночный запрос к нескольким таблицам;
- объединение нескольких запросов, не содержащих подзапросов, с помощью операций типа UNION.
Дальнейшая детализация запросов без подзапросов приведена на рисунке 8.9. Все приведенные на нём разновидности запросов вам уже известны. Лучше будет, если вы внимательно рассмотрите рисунок и по тем видам запросов, которые вы подзабыли, вернётесь к предыдущим разделам.
Перейдём к запросам с подзапросами. Вы, конечно, помните, что любые подзапросы могут помещаться во фразы FROM, WHERE и HAVING (рисунок 8.10). Однако, коррелированные подзапросы могут находиться только во фразах SELECT, WHERE и HAVING. Дело в том, что фраза FROM в запросе обрабатывается первой и постоянные переходы между таблицами, характерные для этого типа подзапросов не соответствуют назначению фразы FROM.
Заметим, что, как правило, транслятор позволяет писать обычные подзапросы во фразе SELECT. Но как-то трудно обосновать полезность результата со столбцом, заполненным одинаковыми значениями.
Шаблоны фраз SELECT, FROM, WHERE и т.д. контекстны. Иначе говоря, они зависят от структуры основного шаблона и, может быть, друг от друга. Поясним это свойство на примере первых семи основных шаблонов. Будем обозначать шаблон первыми буквами ключевых слов инструкции SELECT, например, SF это имя шаблона инструкции SELECT . . . FROM . . .
В первую группу шаблонов входят SF, SFO, SFW и SFWO. Во второй группе SFGO, SFWG и SFWGO.
Шаблоны отличаются не только синтаксисом, но и семантикой. Так минимальный шаблон SF имеет смысл, который можно описать так: "выборка всех строк таблицы/декартова произведения таблиц с вырезанием указанных столбцов, добавлением вычисленных столбцов и столбцов-констант и, возможно переименованием столбцов результата".
В семантику шаблона SFW следует внести "выбор строк" и "соединение таблиц, если их больше одной".
Шаблоны с фразой ORDER BY, кроме прочего, упорядочивают выходной набор.
Шаблоны с фразой GROUP BY дополнительно группируют строки и вычисляют итоговые значения.
Фраза SELECT для первой группы шаблонов может содержать имена столбцов, константы, арифметические выражения, в том числе с однострочными функциями, и псевдонимы, которые переименовывают столбцы результата. Могут использоваться квалифицированные имена столбцов. Для второй группы шаблонов в этот список следует добавить групповые функции.
На самом деле для шаблонов первой группы (и в Cache, и в Oracle) можно использовать ещё групповые функции, но к определению смысла запроса в этом случае следует добавить, что создаваемая группа строк единственная.
Фраза FROM для обеих групп шаблонов содержит список имён таблиц или представлений, разделяемых запятой и, может быть, снабжённых псевдонимами, приписываемыми к именам таблиц через пробелы. Может содержать подфразу, определяющую соединение таблиц (INNER JOIN, OUTER JOIN и т.д.)
Фраза WHERE для обеих групп шаблонов содержит логические выражения, использующие операторы (IN, BETWEEN, LIKE и др.) и заданные на именах столбцов, константах и однострочных функциях от этих операндов. Эти выражения определяют условия выбора строк, и, может быть, условия соединения таблиц. В случае самосоединения использование квалифицированных имён обязательно. Использовать групповые функции во фразе WHERE нельзя, так как предполагается отбор строк, но не групп строк.
Условия соединения во фразах FROM и WHERE в одном запросе не совместимы.
Фраза ORDER BY для обеих групп шаблонов содержит разделённый запятыми список имён столбцов или формул, построенных на этих столбцах и константах.
Фраза GROUP BY для второй группы шаблонов содержит список столбцов по которым производится группирование. Использование агрегатных функций запрещено.
Обратим внимание на связь между фразами SELECT и GROUP BY. Включение столбцов, по которым производится группирование во фразу SELECT не обязательно, но их отсутствие делает ответ малоинформативным.
Расширим систему шаблонов, приведенную на рисунке 8.9 добавив условие отбора групп.
Фраза HAVING обеспечивает отбор групп строк. Содержит условия, которые обязательно должны использовать групповые функции. Без них фраза определяет условие отбора строк, а не их групп и потому может быть заменена условием во фразе WHERE. Предполагается использование HAVING вместе с фразой GROUP BY.
Вы уже понимаете, что шаблонов гораздо больше, чем изображено на последних рисунках. И если вы начали сомневаться, сможете ли вы их запомнить, то спешу обрадовать: скорее нет, чем да.
Вы сейчас находитесь в положении молодого Самюэля Клеменса (Марк Твен), когда от лоцмана Биксби он узнал, что должен помнить все населённые пункты на всей реке Миссисипи. Как вы помните, Биксби успокоил новичка, сказав "Ты парень не беспокойся. Раз я за тебя взялся, я тебя либо убью, либо выучу".
Постараемся обойтись без крайних мер. Тем более, что запоминать все варианты бесполезно. Как всегда в программировании, следует прорешать набор примеров, хорошо покрывающих возможное множество решаемых задач и выработать необходимые образы. После этого достаточно следовать какой-нибудь разумной методике решения задач. Один из возможных вариантов будет предложен ниже.