2009-10-27 109 views
0

使用Oracle 10.2.0。Oracle - 多級自然排序行

我有一個由行號,縮進級別和文本組成的表格。我需要編寫一個例程來'自然'對縮進級別內的文本進行排序[這是縮進級別較低的子節點]。我在分析例程方面經驗有限,並且通過/之前進行連接,但是從我在這裏和其他地方閱讀的內容看來,似乎可以用它們來幫助我的事業,但我無法弄清楚。

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 

其結果是,每個縮進級別按字母順序排序,在其縮進級別內,在縮進級別內。

+0

聽起來像是給我的家庭作業。 – Gandalf 2009-10-27 20:29:36

+0

似乎很難做家庭作業,但誰知道? – Lucero 2009-10-27 20:33:49

+0

它不是一項家庭作業。我已經極大地簡化了表格和數據,專注於答案,而不是無關的數據。 – user55904 2009-10-27 20:48:23

回答

1

這是我的工作。不知道它可能在更大的集合上有多高效。對我而言,困難的部分是完全基於縮進和原始順序來確定給定行的「父」。

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

嘿嘿......幾乎同時發佈。我必須承認,你找到父母的方式更小,我很難... – Lucero 2009-10-27 23:01:21

+0

謝謝!我找不到我的父親!神奇的解決方案! – user55904 2009-10-28 12:57:13

0

好的,在這裏你去。數據結構中的難點在於父對象不是(明確)已知的,因此查詢的第一部分根據規則只會識別父對象(對於每個節點,它將獲得所有子節點一級深度,停止只要標識小於或等於開始節點)。

其餘的很容易,基本上只是一些遞歸與連接,以獲得您想要他們的順序(動態重新編號他們的項目)的項目。

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; 
+0

偉大的思想想象:-) – kurosch 2009-10-27 23:01:44

+0

我想是的(儘管我沒有看到自己是「偉大的心靈」))。反正很有趣。 – Lucero 2009-10-27 23:06:30