Oracle - естественная сортировка строк на нескольких уровнях

StackOverflow https://stackoverflow.com/questions/1633454

  •  06-07-2019
  •  | 
  •  

Вопрос

Использование 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;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top