Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
BD / Урок 12-Рекурсивный SQL.doc
Скачиваний:
56
Добавлен:
20.02.2016
Размер:
721.41 Кб
Скачать

Рекурсивные запросы в стандарте sql

В стандарте SQL–99 было расширено определение запроса. Его синтаксис принял следующий вид:

[фраза_WITH] запрос

Здесь под запросподразумевается стандартное понимание запроса, как мы его изучали до сих пор. В свою очередь фразаWITH, которая может располагаться в начале запроса, используется для достижения следующих целей:

описать подзапросы, которые многократно используются в самом запросе, с тем, чтобы к ним (подзапросам) можно было обращаться в запросе по имени;

описать рекурсивное выполнение запроса.

Эта фраза имеет следующий немного упрощенный синтаксис:

WITH[RECURSIVE] имя_подзапроса_1 [(список_имен_столбцов_1)]AS(подзапрос_1)[характеристики_поиска_1] [,имя_подзапроса_2 (список_имен_столбцов_2)AS(подзапрос_2)[характеристики_поиска_2]]...

Рассмотрим кратко эти два варианта использования фразы WITH.

Определение повторяющихся подзапросов

Рассмотрим следующий запрос.

Запрос. Если средняя зарплата всех преподавателей–профессоров превышает среднюю зарплату доцентов боле, чем на 400, то вывести средние зарплаты профессоров и доцентов.

SELECT Post AS “Должность", AVG(Salary+Rise) AS "Средн.зарплата", (SELECT AVG(Salary+Rise) FROM TEACHER t, DEPARTMENT d, FACULTY f WHERE t.DepFK = d.DepPK AND d.FacFK = f.FacPK AND LOWER(f.Name) = 'информатика') AS "Средн.зарпл. в CS" FROM TEACHER GROUP BY TEACHER.Post HAVING AVG(Salary+Rise) > (SELECT AVG(Salary+Rise) FROM TEACHER t, DEPARTMENT d, FACULTY f WHERE t.DepFK = d.DepPK AND d.FacFK = f.FacPK AND LOWER(f.Name) = 'информатика');

Должность Средн.зарплата Средн.зарпл. в CS---------- -------------- ----------------- доцент 1190,25 1025,91667 профессор 1553,33333 1025,91667

В приведенном выше запросе дважды повторяется подзапрос, во фразах SELECTи HAVING. С помощью фразыWITHэтот подзапрос можно определить один раз и затем использовать его в самом запросе, как это показано ниже.

WITH FAC_AVG AS (SELECT AVG(Salary+Rise) AS AVG_SAL FROM TEACHER t, DEPARTMENT d, FACULTY f WHERE t.DepFK = d.DepPK AND d.FacFK = f.FacPK AND LOWER(f.Name) = 'информатика')

SELECT Post AS "Должность", AVG(Salary+Rise) AS "Средн.зарплата", (SELECT AVG_SAL FROM FAC_AVG) AS "Средн.зарпл. в CS" FROM TEACHER GROUP BY TEACHER.Post HAVING AVG(Salary+Rise) > (SELECT AVG_SAL FROM FAC_AVG);

Должность Средн.зарплата Средн.зарпл. в CS---------- -------------- ----------------- доцент 1190,25 1025,91667 профессор 1553,33333 1025,91667

Рекурсивное выполнение запросов

Давайте рассмотрим следующий фрагмент запроса:

WITH B AS (SELECT ... FROM A ... UNION ALL SELECT ... FROM A,V ...) SELECT ....

Это не рекурсивный запрос. Если теперь мы в нем заменим «V» на «B» то он по форме преобразуется в рекурсивный:

WITH B AS (SELECT ... FROM A ... UNION ALL SELECT ... FROM A,B ...) SELECT....

Чтобы явно указать, что это рекурсивный запрос, а не случайное совпадение, вводится ключевое слово RECURSIVE:

WITH RECURSIVE B AS (SELECT ... FROM A ... UNION ALL SELECT ... FROM A,B ...) SELECT....

Если это ключевое слово используется в том случае, когда контекст запроса не является рекурсивным, то есть:

WITH RECURSIVE B AS (SELECT ... FROM A ... UNION ALL SELECT ... FROM A,V ...) SELECT....

то ключевое слово игнорируется. С другой стороны, если ключевое слово не используется, однако запрос является рекурсивным, то это рассматривается как ошибка.

При вычислении рекурсивного запроса предполагается, что сначала таблица В пустая. При первом рекурсивном выполнении этого запроса таблица В получит результат выполнения подзапроса SELECT ... FROM A ..., так как второй подзапрос даст пустое значение в связи с тем, что он ссылается на пустую таблицу В. Все последующие рекурсивные вычисления используют именно этот второй подзапрос, так как он имеет рекурсивное определение таблицы В через саму себя. На каждом итерационном цикле текущее состояние таблицы В используется для формирования ее нового состояния. Такое вычисление завершается тогда, когда очередной цикл построения таблицы В не приводит к порождению новых строк в этой таблице.(Примечание – в книге имеется более детальное описание интерпретации рекурсивного запроса.)

Рекурсивный вариант фразы WITHв настоящее время вOracleне реализован. Поэтому покажем, как используется рекурсивный WITH в IBM DB2, на материале статьи Дж.Хенника [1].

Пусть у нас имеется следующий рекурсивный запрос в Oracle:

SELECT TchPK, Name, Chief FROM TEACHER START WITH Chief IS NULL CONNECT BY PRIOR TchPK = Chief;

В стандарте SQL(в интерпретации IBM DB2)с использованием фразыWITHон выражается следующим образом:

WITH RECURSIVE REC_TEACHER (TchPK, Name, Chief) AS -- (1) (SELECT parent.TchPK, parent.Name, parent.Chief -- (2) FROM TEACHER parent WHERE parent.Chief IS NULL UNION ALL SELECT child.TchPK, child.Name, child.Chief -- (3) FROM REC_TEACHER parent, TEACHER child WHERE child.Chief = parent.TchPK)

SELECT TchPK, Name, Chief -- (4) FROM REC_TEACHER;

Опишем кратко составляющие этого запроса.

1) Определяется запрос с именем RECURSIVE_TEACHERи с синонимами столбцов той таблицы, которая получается в результате вычисления запроса. Они нужны для дальнейшей ссылки на эти столбцы.

2) Первая часть запроса. Запрос состоит из двух подзапросов, соединенных через UNION ALL. Это подзапрос определяет стартовую позицию вычисления рекурсии. Он равносилен фразеSTART WITH Chief IS NULLв запросе наOracle.

3) Вторая часть запроса. В этом подзапросе реализуется рекурсия. В нем происходит соединение таблицы TEACHERс таблицей рекурсивного запроса фразыWITH. По сути, он выполняет функцию фразыCONNECT BYOracle.

4) Собственно сам запрос, по которому выполняется рекурсивное вычисление. По сути он ничего не делает, кроме как обращается к рекурсивному запросу и выводит его результат.

Рассмотрим еще несколько примеров, которые продемонстрируют возможности использования рекурсивных запросов.