#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. Отлично, я думаю, что это, по крайней мере, заставляет меня двигаться в правильном направлении. Спасибо!