- •Содержание
- •Основные понятия
- •Понятие данных
- •Файловые системы
- •Системы баз данных
- •История развития субд
- •Трехуровневая архитектура ansi/sparc
- •Общая характеристика моделей данных
- •Основные понятия модели данных
- •Представление статических и динамических свойств
- •Общая характеристика структурных компонентов. Множества: домены и атрибуты
- •Общая характеристика структурных компонентов. Отношения: сущности
- •Общая характеристика структурных компонентов. Отношения: связи
- •Общая характеристика ограничений целостности
- •Модель данных «сущность – связь»
- •Уровни представления информации
- •Уровень 1 – информация о сущностях и связях
- •Уровень 2. Структура информации
- •Ограничения целостности в модели сущность-связь
- •Расширенная модель данных сущность-связь: нотация idef1x
- •Реляционная модель данных
- •Базовые структурные компоненты реляционной модели данных
- •Целостная часть реляционной модели данных
- •Языковые средства описания данных
- •Манипуляционная часть реляционной модели данных
- •Подмножество sql для манипулирования данными
- •Примеры написания запросов
- •I. И еще несколько примеров написания запросов из документации [10]
- •Краткая характеристика языка sql pl db2® udb
- •Дополнительные возможности описания ограничений целостности
- •Дополнительные возможности db2
- •Описание данных
- •Манипулирование данными
- •Дополнительные возможности формирования запросов
- •Типы данных, определенные пользователем
- •Функции, определенные пользователем
- •Теория проектирования реляционных баз данных
- •Цели проектирования
- •Функциональные зависимости
- •1. Рефлексивность
- •2. Пополнение
- •3. Транзитивность
- •4. Псевдотранзитивность
- •5. Аддитивность (объединение)
- •6. Декомпозиция (проективность)
- •7. Композиция
- •Нормализация отношений
- •Внутренние структуры хранения
- •Структурная схема обработки запроса
- •Бинарные деревья
- •Многоходовые деревья
- •Сравнение методов индексирования
- •Создание индексов в db2®
- •Организация файлов базы данных в db2®
Дополнительные возможности формирования запросов
Предложение SELECT, определяющее необходимые запросы, обладает большими возможностями. Кроме возможностей, рассмотренных ранее, здесь рассматриваются следующие:
дополнительные возможности группирования;
использование общих табличных выражений;
использование рекурсивных запросов.
Дополнительные возможности группирования
В списке выбора в предложении SELECT, при использовании агрегатных функций, требующих группирование, могут быть использованы разные формы группирования. Как указывалось ранее, конструкция GROUP BY в предложении SELECT имеет следующий вид:
GROUP BY элемент [, элемент …]
В качестве элемента конструкции GROUP BY, кроме выражения группирования, рассмотренного ранее, могут быть использованы еще группирующее_множество и супер_группы, которые добавляют к рассмотренным ранее правилам записи конструкции GROUP BY некоторые дополнительные возможности.
Группирующее множество
Группирующее множество позволяет указать несколько способов группирования данных в одном предложении SELECT и имеет следующий вид:
GROUPING SETS (элемент_группирования [, … ] ) ,
Элемент группирования может быть представлен одним простым элементом или списком простых элементов, заключенным в круглые скобки; каждый простой элемент представляется в виде выражения группирования или супер группы.
Группирующее множество можно рассматривать как объединение двух или более групп строк в один результирующий набор данных. Использование группирующего множества позволяет вычислять необходимые группы за один просмотр базовой таблицы.
Если в конструкции GROUPING SETS указывается несколько элементов группирования, тогда в результат включается несколько групп строк. Каждая группа строк имеет одинаковые значения колонок, определяемых элементом группирования. Не агрегатные колонки из списка выбора SELECT, не включенные в элемент группирования, возвращают значение NULL для каждой строки, сгенерированной для группирующего множества. Это отражает тот факт, что агрегация данных была выполнена без учета значений этих колонок.
Рассмотрим пример. Пусть имеется следующая таблица:
INVOICE |
ACode |
SId |
WId |
Qty |
|
A1 |
S1 |
W1 |
120 |
|
A1 |
S1 |
W2 |
200 |
|
A1 |
S2 |
W1 |
150 |
|
A1 |
S2 |
W2 |
100 |
|
A2 |
S1 |
W1 |
220 |
|
A2 |
S1 |
W2 |
130 |
|
A2 |
S2 |
W1 |
250 |
|
A2 |
S2 |
W2 |
200 |
Пусть необходимо сформировать отчет, в котором отражено суммарное количество поставленного товара для каждого вида товара (ACode) и для каждого поставщика (SId).
Результат может быть получен в одном запросе, если использовать группирующее множество:
SELECT ACode, SId, SUM(Qty) AS Total
FROM INVOICE
GROUP BY GROUPING SETS (ACode, SId)
В данном запросе определены два элемента группирования, каждый из которых определяет свои группы строк. В результате выполнения запроса будут сформированы группы строк двух видов: с одинаковым значением атрибута ACode и с одинаковым значением атрибута SId, и для каждой группы будет вычислена агрегатная функция SUM(Qty). Результат отчета может иметь следующий вид:
ACode |
SId |
Total |
- |
S1 |
670 |
- |
S2 |
700 |
A1 |
- |
570 |
A2 |
- |
800 |
Если нужно указать для одной группы строк несколько атрибутов, такой элемент группирования должен быть заключен в круглые скобки. Если приведенный выше запрос изменить следующим образом:
SELECT ACode, SId, SUM(Qty) AS Total
FROM INVOICE
GROUP BY GROUPING SETS ((ACode, Sid))
В этом случае в группирующем множестве определен только один элемент группирования, который определяет группы строк с одинаковыми значениями атрибутов ACode и SId. Результат такого запроса может иметь следующий вид:
ACode |
SId |
Total |
A1 |
S1 |
320 |
A1 |
S2 |
250 |
A2 |
S1 |
350 |
A2 |
S2 |
450 |
Видно, что данный запрос эквивалентен следующему:
SELECT ACode, SId, SUM(Qty) AS Total
FROM INVOICE
GROUP BY ACode, SId
Рассмотрим следующий запрос:
SELECT ACode, SId, WId, SUM(Qty) AS Total
FROM INVOICE
GROUP BY GROUPING SETS ((ACode, SId), (ACode, WId))
В нем определены два элемента группирования. Первый элемент определяет группы строк с одинаковыми значениями атрибутов ACode и SId, второй – группы строк с одинаковыми значениями атрибутов ACode и WId. Для каждой группы вычисляется функция SUM(Qty). Результат выполнения запроса может быть следующим:
ACode |
SId |
WId |
Total |
A1 |
- |
W1 |
270 |
A1 |
- |
W2 |
300 |
A2 |
- |
W1 |
470 |
A2 |
- |
W2 |
330 |
A1 |
S1 |
- |
320 |
A1 |
S2 |
- |
250 |
A2 |
S1 |
- |
350 |
A2 |
S2 |
- |
450 |
Следует заметить, что группирующее множество определяет фундамент построения результатов при использовании конструкции GROUP BY. Например, конструкция GROUP BY Col1 эквивалентна конструкции GROUP BY GROUPING SETS((Col1)), а конструкция GROUP BY Col1, Col2, Col3 эквивалентна конструкции GROUP BY GROUPING SETS((Col1, Col2, Col3)).
Супер группа
Супер группа задается одним из следующих способов:
ROLLUP (элемент_группирования [, …])
CUBE (элемент_группирования [, …])
( )
Элемент группирования может быть представлен отдельным выражением группирования или списком выражений, заключенным в круглые скобки.
Последний элемент (пустые круглые скобки) определяет общий итог, в соответствии с которым весь результат, полученный при выполнении запроса, рассматривается как одна группа строк.
Конструкция ROLLUP расширяет возможности группирования, определяемые конструкцией GROUP BY, включая в результат запроса дополнительные итоговые строки. Итоговые строки получаются в результате создания, на основе списка, указанного в ROLLUP, дополнительных группирующих множеств, путем последовательного исключения последних элементов из списка. Так, если была определена следующая конструкция: ROLLUP(a, b, c), при ее обработке будут созданы следующие группирующие множества: (a, b, c), (a, b), (a), ().
Таким образом, конструкция ROLLUP, в которой указано n элементов, трансформируется в n + 1 группирующее множество.
Следует отметить, что на результат запроса влияет порядок перечисления элементов в конструкции ROLLUP. Так, при обработке конструкции ROLLUP(a, b) будут созданы группирующие множества (a, b), (a), (), тогда как при обработке конструкции ROLLUP(b, a) будут созданы группирующие множества (b, a) (или (a, b)), (b), ().
Рассмотрим пример. Пусть для приведенной выше таблицы выполняется следующий запрос:
SELECT ACode, SId, SUM(Qty) AS Total
FROM INVOICE
GROUP BY ROLLUP (ACode, SId)
ORDER BY ACode, SId
При выполнении данного запроса будут определены несколько групп; в первую группу включаются строки, имеющие одинаковое значение совокупности атрибутов ACode и SId, во вторую группу – строки с одинаковым значением атрибута ACode и третью группу составят все строки таблицы. Результат выполнения запроса может иметь следующий вид:
ACode |
SId |
Total |
A1 |
S1 |
320 |
A1 |
S2 |
250 |
A1 |
- |
570 |
A2 |
S1 |
350 |
A2 |
S2 |
450 |
A2 |
- |
800 |
- |
- |
1370 |
Конструкция CUBE расширяет возможности группирования, определяемые конструкцией GROUP BY, также включая в результат запроса дополнительные итоговые строки. Однако, в отличие от ROLLUP, дополнительные итоговые строки формируются путем создания всех комбинаций из элементов, указанных в списке CUBE. Так, если была определена следующая конструкция: CUBE(a, b, c), при ее обработке будут созданы следующие группирующие множества: (a, b, c), (a, b), (a, c), (b, c), (a), (b), (c), (). В соответствии с этим, порядок перечисления элементов в конструкции CUBE на результат не влияет.
Таким образом, конструкция CUBE, в которой указано n элементов, трансформируется в 2n группирующих множеств.
Рассмотрим пример. Пусть для приведенной выше таблицы выполняется следующий запрос:
SELECT ACode, SId, SUM(Qty) AS Total
FROM INVOICE
GROUP BY CUBE (ACode, SId)
ORDER BY ACode, SId
При выполнении данного запроса будут определены несколько групп. В первую группу включаются строки, имеющие одинаковое значение совокупности атрибутов ACode и SId, во вторую группу – строки с одинаковым значением атрибута ACode, в третью группу – строки с одинаковым значением атрибута SId, и, наконец, четвертую группу составят все строки таблицы. Результат выполнения запроса может иметь следующий вид:
ACode |
SId |
Total |
A1 |
S1 |
320 |
A1 |
S2 |
250 |
A1 |
- |
570 |
A2 |
S1 |
350 |
A2 |
S2 |
450 |
A2 |
- |
800 |
- |
S1 |
670 |
- |
S2 |
700 |
- |
- |
1370 |
Дополнительные функции
При использовании различных способов группирования в списке SELECT могут быть использованы дополнительные функции – GROUPING и RANK.
Функция GROUPING относится к категории агрегатных функций и имеет следующий синтаксис:
GROUPING(выражение)
Данная функция используется вместе с группирующими множествами и супер группами и позволяет определить, значение указанной выражением колонки имеется в данных таблицы или получено при вычислении итоговых значений при группировании.
Функция возвращает целое значение. Результат функции:
1 – значение выражения в строке результата имеет NULL значение, строка была сгенерирована супер группой;
0 – в противном случае.
Например, пусть имеется следующая таблица EMPL:
EMPNO |
LASTNAME |
SALARY |
JOB |
EDLEVEL |
000120 |
O’Connel |
29250.00 |
Clerk |
14 |
000100 |
Spenser |
26150.00 |
Manager |
14 |
000130 |
Quintana |
23800.00 |
Analyst |
16 |
000280 |
Schneider |
26250.00 |
Operator |
17 |
000250 |
Smith |
19180.00 |
Clerk |
- |
000060 |
Stern |
32250.00 |
Manager |
16 |
Пусть выполняется следующий запрос:
SELECT EDLEVEL, DECIMAL(AVG(SALARY), 8, 2) AS AVG_Salary
FROM EMPL
GROUP BY ROLLUP(EDLEVEL)
Будут получены следующие результаты:
EDLEVEL |
AVG_SALARY |
- |
26146.66 |
14 |
27700.00 |
16 |
28025.00 |
17 |
26250.00 |
- |
19180.00 |
В полученных результатах две строки имеют в колонке EDLEVEL пустое значение, и не понятно, какое из них соответствует строке таблицы, а какое – итоговому значению. Использование функции
GROUPING() позволяет ответить на данный вопрос:
SELECT EDLEVEL, GROUPING(EDLEVEL) AS TP,
DECIMAL(AVG(SALARY), 8, 2) AS AVG_Salary
FROM EMPL
GROUP BY ROLLUP(EDLEVEL)
Будут получены следующие результаты:
EDLEVEL |
TP |
AVG_SALARY |
- |
1 |
26146.66 |
14 |
0 |
27700.00 |
16 |
0 |
28025.00 |
17 |
0 |
26250.00 |
- |
0 |
19180.00 |
Значение 1 в колонке TP в первой строке указывает, что данная строка сгенерирована супер группой (получена при вычислении итоговых значений при группировании), тогда как третья строка получена в результате обработки строк таблицы.
Функция RANK относится к категории OLAP функций и используется для ранжирования результата.
Функция имеет следующий синтаксис:
RANK () OVER ( [ PARTITION BY колонка ] ORDER BY выражение [ ASC | DESC ] )
Функция ранжирования вычисляет ранг строки в пределах некоторого множества строк.
PARTITION BY указывает, в пределах каких строк следует определять ранг. Если конструкция
PARTITION BY отсутствует, тогда ранг вычисляется в пределах всех строк, определяющих результат запроса. Если PARTITION BY указано, тогда значение ранга вычисляется в пределах каждой группы строк, имеющих одинаковое значение указанной колонки.
Строки, не различаемые в пределах данного множества, получают одинаковое значение ранга.
Результат ранжирования может быть определен с учетом или без учета пропусков, появляющихся при обработке нескольких строк с одинаковым значением.
Если указывается RANK, ранг строки определяется как 1 плюс количество строк, которые физически предшествуют данной строке. Поэтому, если несколько строк не отличаются друг от друга с позиций упорядоченности, тогда в последовательности значений рангов будут пропуски.
Вместо RANK можно использовать функцию DENSE_RANK, тогда пропусков не будет.
Пример:
SELECT EMPNO, LASTNAME, SALARY,
RANK() OVER(ORDER BY SALARY DESC) AS SALARY_RANK
FROM EMPLOYEE
ORDER BY EMPNO
Здесь одна группа строк, общий механизм вычисления ранга.
Еще пример:
SELECT WORKDEPT, EMPNO, LASTNAME, EDLEVEL,
DENSE_RANK() OVER(PARTITION BY WORKDEPT
ORDER BY EDLEVEL DESC) AS RANK_EDLEVEL
FROM EMPLOYEE
ORDER BY WORKDEPT, LASTNAME
Здесь для каждого отдела вычисляются свои значения ранга.
Табличные выражения
Ранее рассматривалась возможность использования табличных выражений в конструкции FROM предложения SELECT, например:
SELECT …
FROM ( подзапрос ) AS E
INNER JOIN
( подзапрос ) AS N
ON E.xx = N.xx
В подзапросах, использованных в записи табличных выражений, можно использовать агрегатные функции и группирование.
Наряду с этим, в предложении SELECT могут быть использованы общие табличные выражения. В общем случае, предложение SELECT имеет следующий синтаксис:
WITH
общее_табличное_выражение [, …]
запрос
Правила записи запроса были рассмотрены ранее.
Общее табличное выражение записывается следующим образом:
имя_таблицы [ ( список_колонок ) ] AS ( подзапрос )
Общее табличное выражение позволяет определить таблицу с именем имя_таблицы, которая может быть использована в конструкциях FROM и других следующего за табличным выражением основного запроса. В одной конструкции WITH можно определить несколько табличных выражений; в этом случае имя таблицы может быть указано в любом последующем табличном выражении.
Рассмотрим примеры.
1. В соответствии со структурами таблиц, приведенных в разделе 4.6 (задача Н4), необходимо для каждой торговой базы получить ее название, название и суммарное количество поставленных на базу товаров, а также суммарное количество всех поставленных на данную базу товаров.
С помощью общего табличного выражения данный запрос может быть реализован следующим образом:
WITH
ART(WId, ACode, Qty) AS
(
SELECT WId, ACode, SUM(Qty) FROM INVOICE GROUP BY WId, ACode
),
COM(WId, Qty) AS
(
SELECT WId, SUM(Qty) FROM INVOICE GROUP BY WId
)
SELECT WName, AName, Art.Qty AS SubTotal, Com.Qty AS GrandTotal
FROM WAREHOUSE W, ARTICLE A, ART, COM
WHERE W.WId = ART.WId AND A.ACode = ART.ACode AND W.WId = COM.WId
Или так:
WITH
ART(WId, ACode, Qty) AS
(
SELECT WId, ACode, SUM(Qty) FROM INVOICE GROUP BY WId, ACode
),
COM(WId, Qty) AS
(
SELECT WId, SUM(Qty) FROM ART GROUP BY WId
)
SELECT WName, AName, ART.Qty AS SubTotal, COM.Qty AS GrandTotal
FROM WAREHOUSE W, ARTICLE A, ART, COM
WHERE W.WId = ART.WId AND A.ACode = ART.ACode AND W.WId = COM.WId
2. Вернемся к задаче H1 из раздела 4.6: найти имена поставщиков, поставляющих максимальное суммарное количество товара. В рассмотренном ранее примере использовалось представление.
Использование табличного выражения в конструкции FROM не позволяет получить требуемый результат, так как к этому табличному выражению приходится ссылаться из подзапроса, используемого в конструкции WHERE основного запроса, что недопустимо:
SELECT SName
FROM Salor S,
(SELECT SId, SUM(Qty) FROM Invoice GROUP BY SId) AS V1(SId, Qty)
WHERE S.SId = V1.SId AND
V1.Qty = (SELECT MAX(Qty) FROM V1) –- здесь в подзапросе имя V1 не определено
Проблему решает общее табличное выражение: определяемая им таблица может быть использована и в подзапросе:
WITH
V1(SId, Qty) AS
(
SELECT SId, SUM(Qty) FROM INVOICE GROUP BY SId
)
SELECT SName
FROM SALOR S, V1
WHERE S.SId = V1.SId AND V1.Qty = (SELECT MAX(Qty) FROM V1)
Рекурсивное табличное выражение
Подзапросы, использованные в определении общего табличного выражения, могут ссылаться на это же табличное выражение; в этом случае общее табличное выражение определено как рекурсивное общее табличное выражение.
Рекурсивное общее табличное выражение состоит из двух запросов, объединенных операцией
UNION ALL. Первый запрос в выражении определяется как инициализирующий и не должен ссылаться на это же табличное выражение; второй запрос определяется как итерационный (повторяющийся) и содержит ссылку на это же табличное выражение:
WITH имя_таблицы (список_колонок) AS
(
инициализирующий_запрос
UNION ALL
итерационный_запрос
)
основной_запрос
Инициализирующий запрос выполняется только один раз и определяет начальное состояние таблицы, ассоциированной с общим табличным выражением. Итерационный запрос выполняется многократно и использует строки из таблицы, ассоциированной с общим табличным выражением; результат запроса также записывается в таблицу и используется при следующей итерации выполнения итерационного запроса. Выполнение запроса заканчивается, когда будут обработаны все записи из таблицы, ассоциированной с общим табличным выражением.
Когда разрабатывается рекурсивное общее табличное выражение, следует помнить, что может быть получен бесконечный рекурсивный цикл. Необходимо тщательно проектировать рекурсивное общее табличное выражение, чтобы избежать бесконечного цикла.
Рекурсивные общие табличные выражения могут быть полезны при написании запросов с таблицами, представляющими отношения, для которых определены рекурсивные связи.
Рассмотрим следующий пример.
Пусть определено некоторое отношение КОМПОНЕНТ, для которого определена рекурсивная связь с этим же отношением: каждый компонент, представляя некоторый агрегат, состоит из нескольких (0 или более) компонентов; компонент, являясь частью, входит в несколько (0 или более) других компонентов.
Такое отношение может быть представлено таблицей PARTLIST, имеющей следующие колонки: Part для представления компонента – агрегата, Subpart для представления компонента – части и Quantity для указания того, в каком количестве компонент-часть включен в компонент-агрегат:
CREATE TABLE PARTLIST (
Part VARCHAR(8),
Subpart VARCHAR(8),
Quantity INTEGER
);
Предположим, что таблица PARTLIST имеет следующий вид:
Part |
Subpart |
Quantity |
00 |
01 |
5 |
00 |
05 |
3 |
01 |
02 |
2 |
01 |
03 |
3 |
01 |
04 |
4 |
01 |
06 |
3 |
02 |
05 |
7 |
02 |
06 |
6 |
03 |
07 |
6 |
04 |
08 |
10 |
04 |
09 |
11 |
05 |
10 |
10 |
05 |
11 |
10 |
06 |
12 |
10 |
06 |
13 |
10 |
07 |
14 |
8 |
07 |
12 |
8 |
Пример 1. Написать запрос, позволяющий получить, какие компоненты необходимы для построения некоторого заданного компонента, например, компонента с номером ’01’. Полученный список должен включать все компоненты, входящие в состав заданного (подкомпоненты первого уровня); компоненты, входящие в состав подкомпонентов первого уровня, и т.д. При этом, если некоторый компонент используется несколько раз (как часть разных компонентов), он должен быть включен только один раз.
WITH RPL (Part, Subpart, Quantity) AS
(
SELECT ROOT.Part, ROOT.Subpart, ROOT.Quantity
FROM PARTLIST ROOT
WHERE ROOT.Part = '01'
UNION ALL
SELECT CHILD.Part, CHILD.Subpart, CHILD.Quantity
FROM RPL PARENT, PARTLIST CHILD
WHERE PARENT.Subpart = CHILD.Part
)
SELECT DISTINCT Part, Subpart, Quantity
FROM RPL
ORDER BY Part, Subpart, Quantity;
Приведенный запрос включает общее табличное выражение, названное RPL (Recursive PARTLIST), которое определяет рекурсивную часть запроса. Оно иллюстрирует основные элементы рекурсивного общего табличного выражения. Первый операнд (запрос) операции UNION, определяемый как инициализирующий запрос, позволяет получить компоненты, подчиненные непосредственно компоненту с номером ’01’. В конструкции FROM этого запроса используется исходная таблица и никогда не используется это же табличное выражение (RPL в данном примере). Результат первого запроса записывается в таблицу общего табличного выражения RPL, и RPL будет иметь следующий вид:
Номер строки |
Part |
Subpart |
Quantity |
1 |
01 |
02 |
2 |
2 |
01 |
03 |
3 |
3 |
01 |
04 |
4 |
4 |
01 |
06 |
3 |
Как и в данном примере, всегда должна использоваться операция UNION ALL. Второй операнд (запрос) операции UNION использует RPL для вычисления подкомпонентов для подкомпонентов, указывая в конструкции FROM соединение таблицы общего табличного выражения RPL (PARENT) по атрибуту Subpart и исходной таблицы (CHILD) по атрибуту Part. Результат также записывается в RPL. По мере появления новых строк в RPL они будут использоваться в запросе, определенном во втором операнде UNION.
После первой итерации в табличное выражение RPL будут добавлены строки с 5-й по 11-ю, и просмотр табличного выражения RPL закончится на 4-й строке:
Номер строки |
Part |
Subpart |
Quantity |
1 |
01 |
02 |
2 |
2 |
01 |
03 |
3 |
3 |
01 |
04 |
4 |
4 |
01 |
06 |
3 |
5 |
02 |
05 |
7 |
6 |
02 |
06 |
6 |
7 |
03 |
07 |
6 |
8 |
04 |
08 |
10 |
9 |
04 |
09 |
11 |
10 |
06 |
12 |
10 |
11 |
06 |
13 |
10 |
На второй итерации обработка RPL продолжится с 5-й строки; в результате в RPL будут добавлены строки с 12-й по 17-ю, и просмотр табличного выражения RPL закончится на 11-й строке:
Номер строки |
Part |
Subpart |
Quantity |
1 |
01 |
02 |
2 |
2 |
01 |
03 |
3 |
3 |
01 |
04 |
4 |
4 |
01 |
06 |
3 |
5 |
02 |
05 |
7 |
6 |
02 |
06 |
6 |
7 |
03 |
07 |
6 |
8 |
04 |
08 |
10 |
9 |
04 |
09 |
11 |
10 |
06 |
12 |
10 |
11 |
06 |
13 |
10 |
12 |
05 |
10 |
10 |
13 |
05 |
11 |
10 |
14 |
06 |
12 |
10 |
15 |
06 |
13 |
10 |
16 |
07 |
14 |
8 |
17 |
07 |
12 |
8 |
На следующей итерации обработка RPL продолжится с 12-й строки. В строках с 12-й по 17-ю в колонке Subpart RPL указаны значения, которые отсутствуют в колонке Part основной таблицы, поэтому новые строки в RPL не заносятся; когда все строки RPL будут просмотрены (обработаны), выполнение запроса завершится.
В полученном результате можно заметить, что, например, строка <06, 12, 10> появилась дважды: один раз в результате обработки связи 01 – 06, 06 – 12, второй раз – в результате обработки связи 01 – 02, 02 – 06, 06 – 12. Фраза SELECT DISTINCT в основном запросе гарантирует, что одни и те же значения Part/Subpart не будут выведены более одного раза.
В результате выполнения приведенного запроса будут получены следующие данные:
Part |
Subpart |
Quantity |
01 |
02 |
2 |
01 |
03 |
3 |
01 |
04 |
4 |
01 |
06 |
3 |
02 |
05 |
7 |
02 |
06 |
6 |
03 |
07 |
6 |
04 |
08 |
10 |
04 |
09 |
11 |
05 |
10 |
10 |
05 |
11 |
10 |
06 |
12 |
10 |
06 |
13 |
10 |
07 |
12 |
8 |
07 |
14 |
8 |
Пример 2. Написать запрос, позволяющий получить общее количество каждого компонента, входящего в состав некоторого заданного компонента, например, компонента с номером ’01’.
Главное отличие от примера 1 заключается в том, что здесь необходимо использовать агрегатную функцию для суммирования количества компонентов. Так, например, компонент ’02’ включает 6 экземпляров компонента ’06’. Если компонент ’01’ включает 2 экземпляра компонента ’02’, значит, для построения компонента ’01’, в общем случае, потребуется 2*6 = 12 экземпляров компонента ’06’.
WITH RPL (Part, Subpart, Quantity) AS
(
SELECT ROOT.Part, ROOT.Subpart, ROOT.Quantity
FROM PARTLIST ROOT
WHERE ROOT.Part = '01'
UNION ALL
SELECT PARENT.Part, CHILD.Subpart, PARENT.Quantity *CHILD.Quantity
FROM RPL PARENT, PARTLIST CHILD
WHERE PARENT.Subpart = CHILD.Part
)
SELECT Part, Subpart, SUM(Quantity) AS "Total QTY Used"
FROM RPL
GROUP BY Part, Subpart
ORDER BY Part, Subpart
В приведенном запросе во втором подзапросе рекурсивного общего табличного выражения RPL вычисляется общее количество компонентов, используемых в составе другого компонента. Так как некоторый компонент может входить в состав разных компонентов, необходимо в основном запросе использовать группирование и агрегатную функцию.
В результате выполнения приведенного запроса будут получены следующие данные:
Part |
Subpart |
Total QTY Used |
01 |
02 |
2 |
01 |
03 |
3 |
01 |
04 |
4 |
01 |
05 |
14 |
01 |
06 |
15 |
01 |
07 |
18 |
01 |
08 |
40 |
01 |
09 |
44 |
01 |
10 |
140 |
01 |
11 |
140 |
01 |
12 |
294 |
01 |
13 |
150 |
01 |
14 |
144 |
Так, подкомпонент (Subpart) типа ’06’ входит в состав компонента ’01’ непосредственно в количестве 3 экземпляров, а также через компонент ’02’ в количестве 2 * 6 = 12 экземпляров; в общем случае, в компонент ’01’ входит 15 экземпляров подкомпонента ’06’.
Пример 3. В некоторых случаях нет необходимости обрабатывать данные на всю глубину рекурсии; достаточно ограничиться несколькими первыми уровнями. Например, требуется получить только первые два уровня вложенности компонентов в компонент с номером ’01’. Другими словами, в отчет необходимо включить значение, определяющее глубину рекурсии. Это достигается за счет включения во второй запрос дополнительной вычисляемой колонки вида col + 1 (увеличение значения колонки на 1 при каждой следующей итерации); в инициализирующем запросе в соответствующей колонке должно быть указано начальное значение. Эта колонка может использоваться во втором запросе в конструкции WHERE для ограничения глубины рекурсии.
WITH RPL (Level, Part, Subpart, Quantity) AS
(
SELECT 1, ROOT.Part, ROOT.Subpart, ROOT.Quantity
FROM PARTLIST ROOT
WHERE ROOT.Part = '01'
UNION ALL
SELECT PARENT.Level + 1, CHILD.Part, CHILD.Subpart, CHILD.Quantity
FROM RPL PARENT, PARTLIST CHILD
WHERE PARENT.Subpart = CHILD.Part AND PARENT.Level < 2
)
SELECT Part, Level, Subpart, Quantity
FROM RPL;
Приведенный запрос аналогичен запросу примера 1. В данном запросе в общее табличное выражение добавлена колонка Level для определения глубины рекурсии. В инициализирующем запросе в данную колонку записывается значение 1. Во втором запросе значение колонки Level, считываемое из таблицы общего табличного выражения RPL (PARENT), увеличивается на 1. Для управления глубиной рекурсии во второй запрос в конструкцию WHERE добавлено условие Level < 2. Это условие гарантирует, что будет рассмотрено только два уровня вложенности компонентов.
В результате выполнения приведенного запроса будут получены следующие данные:
Part |
Level |
Subpart |
Quantity |
01 |
1 |
02 |
2 |
01 |
1 |
03 |
3 |
01 |
1 |
04 |
4 |
01 |
1 |
06 |
3 |
02 |
2 |
05 |
7 |
02 |
2 |
06 |
6 |
03 |
2 |
07 |
6 |
04 |
2 |
08 |
10 |
04 |
2 |
09 |
11 |
06 |
2 |
12 |
10 |
06 |
2 |
13 |
10 |
