Oracle - естественная сортировка строк на нескольких уровнях - PullRequest
0 голосов
/ 27 октября 2009

Использование Oracle 10.2.0.

У меня есть таблица, состоящая из номера строки, уровня отступа и текста. Мне нужно написать процедуру для «естественной» сортировки текста на уровне отступа [это дочерний элемент более низкого уровня отступа]. У меня ограниченный опыт работы с аналитическими процедурами и соединениями до / ранее, но из того, что я читал здесь и в других местах, кажется, что они могут быть использованы, чтобы помочь моему делу, но я не могу понять, как.

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  

В результате каждый уровень отступа сортируется в алфавитном порядке, в пределах его уровня отступа, в пределах уровня родительского отступа.

Ответы [ 2 ]

1 голос
/ 28 октября 2009

Вот что я получил на работу. Не знаю, насколько эффективно это может быть на больших сетах. Самой сложной частью для меня было определение «родителя» для данной строки исключительно на основе отступа и исходного порядка.

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
0 голосов
/ 28 октября 2009

Хорошо, вот, пожалуйста. Сложность в вашей структуре данных заключается в том, что родитель не известен (явно), поэтому первая часть запроса ничего не делает, кроме идентификации родителя в соответствии с правилами (для каждого узла он получает все подузлы глубиной на один уровень, останавливаясь как только идентификация станет меньше или равна начальному узлу).

Остальное легко, в основном просто некоторая рекурсия с помощью 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...