Oracle, рекурсивные запросы(или как ето делается)

SQL во всех проявлениях - от ANSI-92 до TSQL.

Модераторы: Yurich, Absurd

Аватара пользователя
Oscar
Сообщения: 963
Зарегистрирован: 29 май 2004, 13:44
Откуда: Мюнхен (рожден в Киеве)
Контактная информация:

Код: Выделить всё

CREATE TABLE direktora (
	id INTEGER PRIMARY KEY,
	direktor INTEGER REFERENCES direktora
	);
INSERT INTO direktora VALUES (4,4);
INSERT INTO direktora VALUES (3,4);
INSERT INTO direktora VALUES (2,3);
INSERT INTO direktora VALUES (1,2);
INSERT INTO direktora VALUES (10,10);
INSERT INTO direktora VALUES (8,10);
INSERT INTO direktora VALUES (7,8 );
INSERT INTO direktora VALUES (9,7);
INSERT INTO direktora VALUES (6,9);
INSERT INTO direktora VALUES (5,6);
SELECT * FROM direktora ORDER BY id;

Код: Выделить всё

        ID   DIREKTOR
---------- ----------
         1          2
         2          3
         3          4
         4          4
         5          6
         6          9
         7          8
         8         10
         9          7
        10         10
id - ID человека
direktor - ID его начальника

Kак получить список всех начальников человека с id = 9 ?


Вообще-то, конечно, интересует общий случай.


Пробовал так:

Код: Выделить всё

SELECT DISTINCT d1.id, d1.direktor
FROM direktora d1,
     direktora d2
WHERE (d1.direktor = d2.id OR d2.direktor = d1.id)
  AND (d1.direktor != d1.id AND d2.direktor != d2.id)
ORDER BY d1.id;
Но как к нему привязать, с какого id начинать?

интересует чистое SQL решение, без PL и Co.

нашел вот еще:

Код: Выделить всё

SELECT id, direktor FROM direktora
START WITH id = 9
CONNECT BY PRIOR id = direktor;
но оно строет дерево вниз от точки начала, а не вверх
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Паапробуй паменять палярность...
CONNECT BY PRIOR director = id;
2B OR NOT(2B) = FF
Аватара пользователя
Oscar
Сообщения: 963
Зарегистрирован: 29 май 2004, 13:44
Откуда: Мюнхен (рожден в Киеве)
Контактная информация:

Absurd, Пробовал ))
Не помогло
Пишет: user data in loop, или что-то в этом роде.

да и PRIOR - я подозреваю, что это праймери ключ, которым ID и является.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Нет это вроде порядок- как спускаться. От id к director или от director к id
2B OR NOT(2B) = FF
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Иерархию через вложенные множества я даже на MySQL делал. Да, для нее нужен join таблицы самой на себя. Но делается это не так.
2B OR NOT(2B) = FF
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Кстати, понял от чего выскакивает ошибка. У директора 10 директор -10, поэтому получается бесконечная рекурсия в недрах Оракла.
2B OR NOT(2B) = FF
Аватара пользователя
Oscar
Сообщения: 963
Зарегистрирован: 29 май 2004, 13:44
Откуда: Мюнхен (рожден в Киеве)
Контактная информация:

Absurd,
да, наверное так.
а как же её остановить?

Дело в том, что таблица Директора связана с таблицей Персонал, обоими колонками по ID персонала.
Потому поле директор в таблице Директора нельзя NULL сделать.


----

Код: Выделить всё

UPDATE direktora SET direktor = NULL WHERE id=direktor;
SELECT id, direktor FROM direktora
START WITH id = 9
CONNECT BY PRIOR direktor = id;
Да, это возвращает именно то, что нужно!
Вот только ... Это не одна таблица на самом деле, и всё взаимосвязано, потому нельзя менять структуру...

Может всё же есть какой-то способ как-то ограничить рекурсию по
WHERE id!=direktor ?....

Мою проблему решил бы оператор NOCYCLE

Код: Выделить всё

CONNECT BY NOCYCLE PRIOR direktor = id
Но в используемой версии (не знаю, какая), Оракле не знает этого оператора :(

Есть идеи?
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

В общем-то если у человека нет вышестоящего директора, то соответствующее поле дожно быть null. Это вполне естественно.

Это естественно даже не для баз данных, а для просто структур данных. В связанном списке последний элемент имеет указатель на следующий равный null. В дереве корневой элемент имеет указатель на предка равный null.

Если поле - foreign key не имеет primary key, которому его нужно сопоставить, то оно всегда делается null. При необходимости оно может быть вытащено через left (или right) join.
Если в базе как-то неверно расставлены констрейнты, то обратись к архитектору.
Если имеем унаследованный код, авторы которого не знали ничего про внешние join'ы , который очень болшой и который нельзя трогать, то тогда можно только посочувствовать.
2B OR NOT(2B) = FF
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

На худой конец можешь вьюху сделать

create view direktora1 as SELECT id, (case when direktor = id then null else direktor end) as direktor FROM direktora;
2B OR NOT(2B) = FF
Аватара пользователя
Oscar
Сообщения: 963
Зарегистрирован: 29 май 2004, 13:44
Откуда: Мюнхен (рожден в Киеве)
Контактная информация:

Absurd, спасибо.

Практическое решение я могу придумать, это не проблема.
Тот же вью, например - это да.

Просто я - фанат невозможного ))
Просто хотелось бы на заданной схеме получить выборку ОДНИМ запросом.
Ответить