Oracle — SQL с рекурсивным предложением WHERE

#sql #database #oracle #oracle11g

#sql #База данных #Oracle #oracle11g

Вопрос:

У меня есть таблица, в которой хранится иерархия рабочих групп в нашей организации. Это выглядит примерно так:

 CREATE TABLE WORKGROUPS  ( 
    WORKGROUPID         NUMBER NULL,
    NAME                VARCHAR2(100) NOT NULL,
    PARENTWORKGROUPID   NUMBER NOT NULL,
    WORKGROUPLEVEL      CHAR(1) DEFAULT 1 NOT NULL,
    CONSTRAINT WORKGROUPS_PK PRIMARY KEY(WORKGROUPID)
)
  

Например, может быть три уровня рабочих групп:

 ID 1 - Sales (PARENTWORKGROUPID = 0)
ID 2 - Business Sales (PARENTWORKGROUPID = 1)
ID 3 - West Coast B2B (PARENTWORKGROUPID = 2)
  

Итак, родительский элемент 3 равен 2, родительский элемент 2 равен 1, а 1 — рабочая группа верхнего уровня без родительского элемента, поэтому мы используем 0.

Теперь у нас есть таблица ЗАДАЧ. Каждая строка ЗАДАЧ имеет столбец WORKGROUPID, который указывает на WORKGROUPID в таблице WORKGROUPS.

Мне нужно написать запрос, который возвращает все ЗАДАЧИ, относящиеся к данной рабочей группе верхнего уровня, например, все, что находится в разделе Sales (в приведенном выше примере это могут быть идентификаторы рабочей группы 1, 2 или 3. По сути, это рекурсивный запрос.

Я могу придумать несколько способов сделать это, используя ЛЕВОЕ СОЕДИНЕНИЕ для проверки каждого уровня, но я бы предпочел держаться подальше от решений, которые жестко задают количество уровней, поскольку база данных рассчитана на любое количество уровней. Любое другое решение, которое я могу придумать, связано с изменением схемы таблицы, чего я не могу сделать в данный момент. Есть идеи? Спасибо!

Ответ №1:

Начиная с Oracle 11gR2, поддерживается ANSI-рекурсивный синтаксис WITH, который является альтернативой START WITH/CONNECT BY:

 WITH wgs ( workgroupid, name ) AS
(
    SELECT workgroupid, name FROM workgroups WHERE workgroupid = :top-lev-dept
     UNION ALL
    SELECT w.workgroupid, w.name FROM workgroups w, wgs WHERE parentworkgroupid = wgs.workgroupid
)
SELECT ...
  

Ключевым моментом здесь является:

 WITH view (column_definition) AS
(
    SELECT root rows
     UNION ALL
    SELECT sub rows FROM table JOIN view ON recursion condition
)
  

Комментарии:

1. Спасибо — я решил START WITH/CONNECT BY использовать метод с CTE, в основном, чтобы узнать что-то новое, а также это потребовало минимальных изменений в представлении, из которого я получал эти данные.

Ответ №2:

У Oracle, похоже, есть своя собственная версия рекурсии (я использую SQL Server и DB2, которые используют общие табличные выражения для управления рекурсией), но я думаю, что это должно получить то, что вы хотите:

 WITH WGS (
    WORKGROUPID,
    NAME
) AS (
    SELECT WORKGROUPID,
        NAME
    FROM WORKGROUPS
    START WITH WORKGROUPID = :top-lev-dept
    CONNECT BY WORKGROUPID = PRIOR PARENTWORKGROUPID
)

SELECT DISTINCT T.*
FROM TASKS
INNER JOIN WGS W
    ON (T.WORKGROUPID = W.WORKGROUPID)
  

Вы, очевидно, перейдете :top-lev-dept в отдел, который ищете.

Если это не так, вы можете проверить справочную страницу Oracle по иерархическим запросам. Это может помочь вам начать в правильном направлении…

Комментарии:

1. Потрясающе, да, похоже, что это «connect by» является ключевым.. Я проведу дополнительные исследования по этой теме и перейду оттуда — спасибо за указатель!

2. В основном это оказалось почти именно тем, что я сделал. Я пытался использовать CONNECT_BY_ROOT, но, похоже, это не сработало с тем, как я присоединялся к материалу. Поэтому я вместо этого использовал CTE и обновил свое представление, чтобы вернуть «корневую» рабочую группу для всех строк. Теперь я могу просто выбрать из этого представления и запросить конкретные корневые рабочие группы, easy и pie.

Ответ №3:

Вы захотите изучить START WITH/CONNECT BY синтаксис.

Что-то вроде:

 select * from workgroups
start with parentworkgroupid = 0
connect by prior workgroupid = parentworkgroupid;
  

Полностью непроверенный, но я думаю, что это поможет вам начать.

Надеюсь, это поможет.

Комментарии:

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