Oracle regexp_substr hierarchal

时间:2015-02-13 15:50:32

标签: regex oracle hierarchical-data

我认为直到今天才理解正则表达式。

(我也在OTN上发布了这个,但我认为在stackoverflow上有更广泛的受众)

我有一个包含大量代码的列,第二列略微提示了层次结构应该是什么。

CREATE TABLE REGEXTEST
  ( ITEM VARCHAR(200),LEV INT);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4',1);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-1',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-10',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-11',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-12',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-13',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-14',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-15',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-15-59A7',3);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-15-59D7',3);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-15-59F7',3);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('4245-4-15-5987',3);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('1090-81/5285',1);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('1090-81/5285-20',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('1090-81/5285-30',2);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('1090-81/5285-30-5',3);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('1090-81/5285-30-5/20',3);

  INSERT INTO REGEXTEST (ITEM,LEV) VALUES ('1090-81/5285-30-6/25',3);

我需要构建父/子的层次结构,其中父级由级别确定。更复杂的是,有时候会有一个斜线/分隔级别,有时会出现连字符。但连字符和破折号并不一定意味着水平的变化。

我将解释最简单的场景(使用4245样本数据)。

4245-4是第一级。都好。 4245-4-1是2级。所以孩子的名字是4245-4-1,其父母的名字是4245-1。我希望结果是ParentColumn,ChildColumn。我正在使用这些数据构建目录结构。所以继续使用相同的例子:

Parent  Child

4245-4

4245-4  4245-4-1

4245-4  4245-4-10

4245-4  4245-4-11

4245-4  4245-4-12

4245-4  4245-4-15

4245-4-15   4245-4-15-59A7

4245-4-15   4245-4-15-59D7

4245-4-15   4245-4-15-59F7

现在更复杂的例子(有/涉及)

Parent  Child

1090-81

1090-81 1090-81/5285

1090-81/52851090-81/5285-20

1090-81/5285    1090-81/5285-30

1090-81/5285-30 1090-81/5285-30-5

1090-81/5285-30 1090-81/5285-30-5/20

1090-81/5285-30 1090-81/5285-30-6/25

没有确定的最大级别数。我认为我目前看到的最大值是6,但我认为这可能会改变。

3 个答案:

答案 0 :(得分:0)

要遵循的一种方法是分层查询。以下查询表示第一个近似值(等待AlexPoole问题的答案):

  SELECT DISTINCT *
    FROM (
              SELECT (SELECT (max(lev)) FROM regextest) - LEVEL  lvl
                   , SUBSTR(item, 1, INSTR(item,'-',-1)-1)       parent
                   , item                                        child
                FROM regextest
          START WITH lev = (SELECT (max(lev)) FROM regextest)
          CONNECT BY PRIOR SUBSTR(item, 1, INSTR(item,'-',-1)- 1) = item
         )
   WHERE lvl > 0
order by child
       ;

答案 1 :(得分:0)

既然你说你对正则表达式有一些了解(你甚至将表格命名为regextest),这里有一个使用正则表达式的解决方案 -

SELECT DISTINCT *
  FROM (    SELECT PRIOR item AS parentitem, item
              FROM regextest
        CONNECT BY PRIOR lev = lev - 1
               AND REGEXP_SUBSTR (item,
                                  '([0-9A-Z/-]+)([-])([0-9A-Z/]+)',
                                  1,
                                  1,
                                  'i',
                                  1) = PRIOR item)
   WHERE parentitem IS NOT NULL
ORDER BY item;

正则表达式拆分'项目'串成3部分:

  1. [直到最后一个连字符的所有内容],
  2. [项目字符串中的最后一个连字符]和
  3. [最后一个连字符后的所有内容]。
  4. 我确定你可以写一个更干净的正则表达式来做同样的事情。

    然后只需将第一个子部分与' PRIOR项目匹配即可。在连接中。匹配的前一项直到最后一个连字符为父项。 可以从第6个参数regex_substr中提取第一个子部分,在本例中为1。

    您可以省略

    中的connect by子句中的部分
    PRIOR lev = lev - 1
    

    因为这只是语义而且没有添加任何值。可能更糟糕的是将其保留为该列中的错误数据会使结果陷入混乱。

    此外,正如另一个答案指出的那样,Alex Poole的问题仍然存在。为什么示例中的1090-81作为一行不在表中?

答案 2 :(得分:0)

我从我所属的一个小组询问的人提出了以下答案:

SELECT    p.item     AS parent
,         c.item     AS child
,         c.lev
FROM             regextext  c
LEFT  JOIN  regextext p  ON   p.lev    = c.lev - 1 
AND  REPLACE (c.item, '-', '/' )
LIKE REPLACE (p.item, '-', '/') || '/%';

我花了86分钟来对抗我的真实数据,但结果正是我所寻找的。