Oracle - естественная сортировка строк на нескольких уровнях
Вопрос
Использование Oracle 10.2.0.
У меня есть таблица, которая состоит из номера строки, уровня отступа и текста.Мне нужно написать процедуру для "естественной" сортировки текста в пределах уровня отступа [который является дочерним элементом более низкого уровня отступа].У меня ограниченный опыт работы с аналитическими процедурами и connect by / prior, но из того, что я прочитал здесь и в других местах, кажется, что их можно было бы использовать, чтобы помочь моему делу, но я не могу понять, как.
CREATE TABLE t (ord NUMBER(5), indent NUMBER(3), text VARCHAR2(254));
INSERT INTO t (ord, indent, text) VALUES (10, 0, 'A');
INSERT INTO t (ord, indent, text) VALUES (20, 1, 'B');
INSERT INTO t (ord, indent, text) VALUES (30, 1, 'C');
INSERT INTO t (ord, indent, text) VALUES (40, 2, 'D');
INSERT INTO t (ord, indent, text) VALUES (50, 2, 'Z');
INSERT INTO t (ord, indent, text) VALUES (60, 2, 'E');
INSERT INTO t (ord, indent, text) VALUES (70, 1, 'F');
INSERT INTO t (ord, indent, text) VALUES (80, 2, 'H');
INSERT INTO t (ord, indent, text) VALUES (90, 2, 'G');
INSERT INTO t (ord, indent, text) VALUES (100, 3, 'J');
INSERT INTO t (ord, indent, text) VALUES (110, 3, 'H');
Это:
SELECT ord, indent, LPAD(' ', indent, ' ') || text txt FROM t;
...возвращает:
ORD INDENT TXT
---------- ---------- ----------------------------------------------
10 0 A
20 1 B
30 1 C
40 2 D
50 2 Z
60 2 E
70 1 F
80 2 H
90 2 G
100 3 J
110 3 H
выбрано 11 строк.
В случае, который я определил для вас, мне нужно, чтобы моя процедура установила ORD 60 = 50 и ORD 50 = 60 [перевернуть их], потому что E находится после D и перед Z.
То же самое с ORD 80 и 90 [с 90, приносящим с собой 100 и 110, потому что они принадлежат к нему], 100 и 110.Конечный результат должен быть:
ORD INDENT TXT
10 0 A
20 1 B
30 1 C
40 2 D
50 2 E
60 2 Z
70 1 F
80 2 G
90 3 H
100 3 J
110 2 H
В результате каждый уровень отступа сортируется в алфавитном порядке, в пределах своего уровня отступа, в пределах родительского уровня отступа.
Решение
Вот над чем мне нужно поработать.Понятия не имею, насколько это может быть эффективно на больших наборах.Самым сложным для меня было определить "родительский элемент" для данной строки, основываясь исключительно на отступе и исходном порядке.
WITH
a AS (
SELECT
t.*,
( SELECT MAX( ord )
FROM t t2
WHERE t2.ord < t.ord AND t2.indent = t.indent-1
) AS parent_ord
FROM
t
)
SELECT
ROWNUM*10 AS ord,
indent,
rpad( ' ', LEVEL-1, ' ' ) || text
FROM
a
CONNECT BY
PRIOR ord = parent_ord
START WITH
parent_ord IS NULL
ORDER SIBLINGS BY
text
Другие советы
Ладно, держи.Трудная часть в вашей структуре данных заключается в том, что родительский элемент не известен (явно), так что первая часть запроса ничего не делает, кроме идентификации родительского элемента в соответствии с правилами (для каждого узла он получает все подузлы на один уровень глубиной, останавливаясь, как только идентификатор становится меньше или равен начальному узлу).
Остальное несложно, в основном просто некоторая рекурсия с помощью connect by для получения элементов в нужном вам порядке (динамически перенумеровывая их).
WITH OrdWithParentInfo AS
(SELECT ID,
INDENT,
TEXT,
MIN(ParentID) ParentID
FROM (SELECT O.*,
CASE
WHEN (CONNECT_BY_ROOT ID = ID) THEN
NULL
ELSE
CONNECT_BY_ROOT ID
END ParentID
FROM (SELECT ROWNUM ID,
INDENT,
TEXT
FROM T
ORDER BY ORD) O
WHERE (INDENT = CONNECT_BY_ROOT INDENT + 1)
OR (CONNECT_BY_ROOT ID = ID)
CONNECT BY ((ID = PRIOR ID + 1) AND (INDENT > CONNECT_BY_ROOT INDENT)))
GROUP BY ID,
INDENT,
TEXT)
SELECT ROWNUM * 10 ORD, O.INDENT, O.TEXT
FROM OrdWithParentInfo O
START WITH O.ParentID IS NULL
CONNECT BY O.ParentID = PRIOR ID
ORDER SIBLINGS BY O.Text;