Опубликован: 28.12.2011 | Доступ: свободный | Студентов: 7487 / 958 | Оценка: 3.81 / 3.53 | Длительность: 19:30:00
ISBN: 978-5-9963-0488-2
Лекция 8:

Соединения таблиц в предложении SELECT

Обработка зациклености данных

Пример организации зациклености в сведениях о маршрутах:

INSERT INTO way VALUES ( 'Новгород', 'Выборг', 135 );

Реакция на появление цикла (уже получается не иерархия) в этом случае отлична от имевшейся для CONNECT BY и будет

ERROR:
ORA-32044: cycle detected while executing recursive WITH query

Упражнение. Проверьте это самостоятельно.

Для предупреждения зацикливания вычислений вводится специальное указание CYCLE, где следует указать перечень (в общем случае) столбцов для распознавания хождения по кругу, придумать название столбца-индикатора (он автоматически включается в конечный ответ) и задать пару символов: для обозначения незацикленной строки и для обозначения строки, где было зафиксировано повторение значений в различительных столбцах:

WITH stepbystep ( node, route, distance ) AS (
  SELECT node, parent || '-' || node, distance
  FROM   way
  WHERE  parent = 'Москва'
     UNION ALL
  SELECT w.node
       , s.route || '-' || w.node
       , w.distance + s.distance
  FROM way w
       INNER JOIN
       stepbystep s
       ON ( s.node = w.parent )
  )
  CYCLE node SET cyclemark TO 'X' DEFAULT '-'
SELECT route, distance, cyclemark FROM stepbystep
/

Ответ:

ROUTE                                        DISTANCE C
------------------------------------------ ---------- -
Москва-Ленинград                                  696 -
Москва-Новгород                                   538 -
Москва-Новгород-Ленинград                         717 -
Москва-Ленинград-Выборг                           831 -
Москва-Новгород-Ленинград-Выборг                  852 -
Москва-Ленинград-Выборг-Новгород                  966 -
Москва-Ленинград-Выборг-Новгород-Ленинград       1145 X
Москва-Новгород-Ленинград-Выборг-Новгород         987 X
Упорядочение результата

Для придания порядка строкам результата в запросах с CONNECT BY используется собственная конструкция ORDER BY SIBLINGS. Аналогичным образом в вынесенном рекурсивном запросе применяется особое указание SEARCH. В его рамках программистом задается в том числе вымышленное имя столбца, в котором СУБД автоматически проставит числовые значения и который самостоятельно включит в порождаемый набор столбцов. На этот столбец программист может сослаться далее уже в обычной фразе ORDER BY для создания нужного порядка строк.

Пример:

ROLLBACK;
WITH stepbystep ( node, route, distance ) AS (
  SELECT node, parent || '-' || node, distance
  FROM   way
  WHERE  parent = 'Москва'
     UNION ALL
  SELECT w.node
       , s.route || '-' || w.node
       , w.distance + s.distance
  FROM way w
       INNER JOIN
       stepbystep s
       ON ( s.node = w.parent )
  )
  SEARCH DEPTH FIRST BY node DESC SET orderval
SELECT   route, distance, orderval 
FROM     stepbystep
ORDER BY orderval DESC
/

Ответ:

ROUTE                                      DISTANCE   ORDERVAL
---------------------------------------- ---------- ----------
Москва-Ленинград-Выборг                         831          5
Москва-Ленинград                                696          4
Москва-Новгород-Ленинград-Выборг                852          3
Москва-Новгород-Ленинград                       717          2
Москва-Новгород                                 538          1

Подробности и прочие свойства построений указания SEARCH приведены в документации по Oracle.

Замечание об общей формулировке запроса

Общая формулировка рекурсивного запроса в стандарте SQL и в Oracle способна вызвать у некоторых программистов недоумение, однако она имеет свое вероятное обоснование. Ранее упоминалось о возможности описания реляционной БД средствами логики предикатов. В таком случае база представляет собой набор истинных утверждений. Пусть есть "предикатный символ" (predicate symbol, то есть "обозначение утверждения") way ( x, y ) как общее обозначение однотипных утверждений (километраж и вероятные другие свойства здесь для простоты опущены как несущественные). В БД представлено несколько конкретных соответствующих ему истинных утверждений, например:

way ( 'Ленинград ', 'Выборг ' )
way ( 'Новгород ', 'Ленинград ' )
...

То есть "имеется путь от Ленинграда до Выборга", "от Новгорода до Ленинграда" и так далее. Это так называемое "существовательное" (intensional) определение БД, явно перечисляющее объекты с их свойствами. Дополнительно можно ввести еще один предикатный символ route (x, y) со смыслом "маршрут". Допустим, что утверждения для него представлены не "существовательно", а "расширительно" (extensionally), в виде двух правил вывода:

route ( x, y ) → way ( x, y )
route ( x, y ) → route ( x, z ), way ( z, y ) 

Это позволяет получать из БД сведения (о "маршрутах"), напрямую в ней не представленные, и БД становится "расширительной". Так, она оказалась дополнена группой новых утверждений вида route ( x, y ).

Теперь если обозначить way ( x, y ) как W, route ( x, y ) как R, второе (рекурсивное) правило вывода как \bullet, то с позиций уже реляционной алгебры для новых сведений из БД для определения маршрутов можно предложить формулировку  R = W \cup R \bullet W^* 1 Идея получения подобной формулы упоминается в книге Марков А. С., Лисовский К. Ю. Базы данных: Введение в теорию и методологию. // М.: Финансы и Статистика, 2006, интересной разработчику и программисту БД во многих других отношениях.. Она удивительно напоминает общее построение рекурсивного запроса в SQL, где однако пошли дальше и обобщили операцию \cup объединения множеств на упомянутую группу. Если обобщенную множественную операцию указать как \circ, формула будет выглядеть как R = W \circ R \bullet W.

Получается, что рекурсивная формулировка запроса в SQL придает вообще-то "существовательной" базе, предполагаемой этим языком, некоторые качества "расширительной", где возможно получение новых "знаний" из имеющихся. К сожалению, на практике такое достижение нельзя подкрепить созданием представления данных (view) на основе рекурсивного запроса ввиду имеющегося в настоящее время в Oracle запрета на подобное действие.

Оборотной стороной помимо риска зацикленности (для избежания которого Oracle дает упоминавшееся частичное решение) является пониженная производительность вычисления. Для повышения производительности Oracle тоже предлагает определенную гамму решений (организация materialized view и прочее), но все они носят неполный характер и обременены собственными издержками.

Ярослав Прозоров
Ярослав Прозоров

В лекции № 7 "Введение в Oracle SQL" в подразделе "Несамостоятельность группировки с обобщениями ROLLUP, CUBE и GROUPING SETS"  представленная таблица сравнения содержит ошибки - окончания запросов пропущены. Видимо, ошибки вызваны некорректным переносом материала лекции.

Володимир Миколайчук
Володимир Миколайчук
Помогите разобраться поетапно с логикой запроса
-------TOOLS
NAME PRICE TYPE
drill 155 A
sawzall 192 N
mitre saw 292 M
router 86 I
RAD 145 M
jigsaw 128 I
screwdriver 77 P
------TOOL_TYPES
TYPE USAGE
A Always
I Often
M Sometimes
N Rarely
P Never

Запрос SQL:
SELECT t.type, SUM(t.price)
FROM tools t
GROUP BY t.type
HAVING SUM(t.price) >= (SELECT AVG(price)
FROM tools
WHERE type IN (SELECT type
FROM tool_types
WHERE usage = 'Often'));

И сколько строк он все таки вернет