【问题标题】:Oracle - natural sort rows on multiple levelsOracle - 在多个级别上自然排序行
【发布时间】:2009-10-27 20:27:31
【问题描述】:

使用 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  

结果是每个缩进级别按字母顺序排序,在其缩进级别内,在父缩进级别内。

【问题讨论】:

  • 听起来像是我的家庭作业。
  • 对于家庭作业来说似乎是硬编码,但谁知道呢?
  • 这不是家庭作业。我已经大大简化了表格和数据,以专注于答案,而不是不相关的数据。

标签: sql oracle plsql oracle10g


【解决方案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

【讨论】:

  • 呵呵...几乎同时发帖。我必须承认,你找到父母的方式要小得多,我很难接受……
  • 谢谢!我无法找到父母!很棒的解决方案!
【解决方案2】:

好的,给你。数据结构中的困难部分是父节点不(明确)知道,因此查询的第一部分除了根据规则识别父节点之外什么都不做(对于每个节点,它让所有子节点深一层,停止只要标识小于或等于起始节点)。

剩下的很简单,基本上只是通过 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;

【讨论】:

  • 我猜是这样(尽管我不认为自己是“伟大的头脑”;))。反正很有趣。
猜你喜欢
  • 1970-01-01
  • 2017-10-20
  • 1970-01-01
  • 1970-01-01
  • 2012-02-28
  • 1970-01-01
  • 2021-07-18
  • 2014-06-22
  • 2017-09-13
相关资源
最近更新 更多