| Каталог | Индекс раздела |
| Назад | Оглавление | Вперед |
Данные, имеющие иерархическую структуру, очень плохо представляются в реляционной модели. В стандарте SQL/92 нет средств для их обработки.
Классическая задача иерархической структуры - спецификация материалов: изделие состоит из неатомарных узлов, которые в свою очередь состоят из узлов и т.д. Именно такую задачу рассматривает К. Дейт [2] и показывает, что средствами самого SQL ее решить нельзя. Решение требует рекурсии - выбрать узел, который лежит в корне, затем выбрать узлы-наследники выбранного узла, затем выбрать их наследников и т.д. Рекурсия в решении Дейта обеспечивается процедурными средствами - встраиванием SQL в программу на языке процедурного программирования.
Обе наши СУБД предоставляют в своих диалектах SQL средства для решения этой задачи. В описании DB2 рассматривается упомянутая задача спецификации материалов, но мы в наших примерах будем использовать прикладную область Oracle - иерархию должностей в "Корпорации Кинга". В таблице employee этой базы данных заложена иерархическая структура подчиненности в фирме: в строке таблицы, описывающей каждого сотрудника, есть столбец manager_id, содержащий код его непосредственного начальника. Начальник, однако, также является сотрудником, для него есть своя строка в таблице employee. Столбцы manager_id всех его подчиненных содержат значение столбца employee_id начальника. У начальника есть в свою очередь начальник и т.д. Только в строке президента фирмы поле manager_id содержится значение NULL.
Таким образом, структура данных в принципе дает возможность получить информацию типа "выбрать структуру подчиненности Корпорации Кинга", но средства стандартного SQL не предоставляют инструментов для выполнения подобных запросов.
Обе рассматриваемые СУБД имеют собственные средства выполнения иерархических запросов.
3.5.1.1 Иерархические запросы в Oracle обеспечиваются фразой Oracle формирует иерархическую выборку, выполняя следующие шаги:
Если запрос содержит фразу Оператор Выражение Чтобы найти дочерние строки, Oracle вычисляет Если 3.5.1.2 Запрос: выбрать фамилии всех прямых начальников сотрудника по фамилии Обработка иерархии обеспечивается выражением 3.5.1.3 Запрос: вывести структуру подчиненности в фирме.
В решении используется условие 3.5.2.1 DB2 позволяет создавать временные представления. Временные представления являются виртуальными таблицами, создающимися только для данного запроса и существующими только на время выполнения этого запроса, но в запросе к временным представлениям можно обращаться, как и к реальным таблицам. С учетом временных представлений синтаксис оператора SELECT в DB2 расширяется, как показано на рис.3.23
Такое представление может, прежде всего, использоваться как способ сокращения запроса. Так, решение запроса, представленного в пп.3.3.4.6, в DB2 может выглядеть так:
В этом решении создается временное представление Однако функциональность временных представлений этим не исчерпывается. Запросы, формирующие временные представления, могут быть рекурсивными, эта возможность и является инструментом для построения иерархических запросов.
3.5.2.2 Запрос: выбрать фамилии всех прямых начальников сотрудника по фамилии Временное представление определяется запросом, содержащим объединение. Первый вложенный запрос в объединении выбирает самого Во всех рекурсивных запросах объединение должно производиться операцией 3.5.2.3 Запрос: вывести структуру подчиненности в фирме.
Временное представление определяется запросом, содержащим объединение. В первом вложенном запросе объединения выбирается президент, во втором - все подчиненные тех, кто был выбран на предыдущем шаге. Псевдостолбец 3.5.1 Иерархические запросы в Oracle
CONNECT BY в операторе SELECT. Эта фраза употребляется в запросе после фразы WHERE и имеет синтаксис, показанный на рис.3.22.

Рисунок 3.22 - Синтаксис фразы CONNECT BY Oracle
START WITH.
CONNECT BY по отношению к одной из корневых строк.
CONNECT BY относительно текущей родительской строки.
WHERE, исключаются все строки, которые не удовлетворяют условию в фразе WHERE. Oracle вычисляет эти условия для каждой строки, а не просто удаляет всех потомков строки, которая не удовлетворяет условию.
SELECT, выполняющий иерархический запрос, не может содержать соединение.
START WITH - задает строку/строки, лежащие в корне иерархии. Это выражение определяет условие, которому должны соответствовать корневые строки. Условие может содержать вложенные запросы. Если эта фраза не задана, то все строки таблицы являются корневыми.
CONNECT BY - задает отношение между родительскими и дочерними строками в иерархии. Отношение задается "P-условием", это может быть любое сравнение, но какая-то его часть должна содержать ключевое слово PRIOR, относящееся к родительской строке.
PRIOR-выражение для родительской строки, а другое выражение - для каждой строки таблицы. Строки, для которых это выражение дает истину, являются дочерними. CONNECT BY может содержать и другие условия-фильтры. CONNECT BY не может содержать вложенных запросов.
CONNECT BY приводит к петле, Oracle возвращает ошибку.
ADAMS.
SELECT employee_id, last_name, manager_id
FROM employee
CONNECT BY PRIOR manager_id=employee_id
START WITH last_name= 'ADAMS'
CONNECT BY, которое выполняет рекурсивную выборку строк: условие, по которому выбирается следующая строка, определяется значениями, выбранными в составе текущей строки. В нашем случае следующей выбирается строка, в которой значение manager_id равно значению employee_id в только что выбранной строке. Выражение START WITH определяет условие выборки первой строки.
SELECT level, employee_id, last_name, manager_id
FROM employee
CONNECT BY PRIOR employee_id=manager_id
START WITH last_name=
(SELECT last_name
FROM employee, job
WHERE employee.job_id=job.job_id
AND function ='PRESIDENT' )
CONNECT BY, инвертированное по сравнению с предыдущей задачей. В этом случае следующей выбирается строка, в которой значение employee_id равно значению manager_id в только что выбранной строке, что обеспечивает движение от корня дерева вниз. Начальной строкой является та, которая содержит код должности, соответствующий функции PRESIDENT. Выборки Oracle, использующие иерархические свойства запросов, могут использовать псевдостолбец level. Этот псевдостолбец имеет значение 1 для узла дерева, находящегося в корне, 2 - для узлов, являющихся непосредственными потомками корневого, и т.д.
3.5.2 Временные представления и иерархические запросы в DB2

Рисунок 3.23 - Полный синтаксис оператора SELECT DB2
WITH tmp AS
(SELECT product_id, SUM(quantity) sss
FROM item, sales_order
WHERE YEAR(order_date)=1990
GROUP BY product_id)
SELECT description, sss
FROM product, tmp
WHERE tmp.product_id=product.product_id
AND sss=(SELECT MAX(sss) FROM tmp)
tmp, формирующееся как результат запроса на выборку суммарного количества проданных в 1990 году экземпляров каждого товара. В основном запросе вместо повторения этого вложенного запроса указывается имя tmp.
ADAMS.
WITH temp (name, id, mid) AS
(SELECT last_name, employee_id, manager_id
FROM employee
WHERE last_name= 'ADAMS'
UNION ALL
SELECT last_name, employee_id, manager_id
FROM employee, temp
WHERE employee_id=mid )
SELECT name, id, mid
FROM temp
ADAMS, второй - соединение таблицы, полученной на предыдущем шаге с таблицей employee по условию employee_id=manager_id. Основной запрос просто выбирает столбцы из временного представления.
UNION ALL, а не просто UNION.
WITH temp (level, name, id, mid) AS
(SELECT 1 AS level, last_name,
employee_id, manager_id
FROM employee
WHERE last_name=
(SELECT last_name
FROM employee, job
WHERE employee.job_id=job.job_id
AND function ='PRESIDENT' )
UNION ALL
SELECT level+1 AS level, last_name,
employee_id, manager_id
FROM employee, temp
WHERE manager_id=id )
SELECT level, name, id, mid
FROM temp
level, имеющийся в Oracle, в DB2 отсутствует, но мы его вводим сами, в каждой следующей итерации его значение увеличивается на 1.
| Назад | Оглавление | Вперед |
| Каталог | Индекс раздела |