2012-04-30 5 views
0

У меня есть данные, что выглядит следующим образом:Ссылка на родительские столбцы в Oracle CONNECT BY иерархического запроса

KEY1 KEY2  KEY3 LKEY1 LKEY2  LKEY3 FLAG 
====== ========= ====== ====== ========= ====== ===== 
09/10 10000  A1234 09/10 AU00A1234 1 
09/10 10000  A1234 09/10 AU000456 A1234 1 
09/10 10000  A1234 09/10 AX000001 A1234 1 
09/10 AX000001 A1234 09/10 AE000010 A1234 0 
09/10 AX000001 A1234 09/10 AE000020 A1234 0 
09/10 AX000001 A1234 09/10 AE000030 A1234 0 
09/10 10000  A1234 09/10 AX000002 A1234 0 
09/10 AX000002 A1234 09/10 AE000040 A1234 0 
09/10 10000  A1234 09/10 AU000789 A1234 0 

Это иерархические данные, в результате чего я бы запрашивающие против ключа корневых композитного (в данном случае 09/10 10000 A1234); поле FLAG относится к «объекту», идентифицированному ключами LKEYx. Может быть любое количество уровней гнездования. (Обратите внимание, что KEY1 и KEY3 полей не должны быть инвариантны, как в приведенном выше примере, до тех пор, пока сохраняется иерархия.)

То, что я хочу, чтобы получить являются листовыми узлами, но если родитель коки KEY2 - та же длина, что и LKEY2, или содержит X в качестве второго символа, а затем возвращает непосредственного родителя. В этом случае, мы должны также отметить запись как по желанию ... Так, что-то вроде этого:

KEY1 KEY2  KEY3 OPTION FLAG 
====== ========= ====== ======= ===== 
09/10 AU00A1234 0  1 
09/10 AU000456 A1234 0  1 
09/10 AX000001 A1234 1  1 
09/10 AX000002 A1234 1  0 
09/10 AU000789 A1234 0  0 

Я написал запрос, который делает это, но это не красиво. Более того, он делает предположение, что все листовые узлы находятся на одном уровне с деревом, чтобы отличать необязательные записи; однако это не обязательно так. Мой запрос выглядит следующим образом:

with queryKeys as (
    select '09/10' key1, 
     '10000' key2, 
     'A1234' key3, 
    from dual 
), 
subTree as (
    select  tree.key1, 
      tree.key2, 
      tree.key3, 

      tree.lkey1, 
      tree.lkey2, 
      tree.lkey3, 

      tree.flag, 

      connect_by_isleaf isLeaf, 
      level thisLevel 

    from  tree, 
      queryKeys 

    start with tree.key1 = queryKeys.key1 
    and  tree.key2 = queryKeys.key2 
    and  tree.key3 = queryKeys.key3 

    connect by tree.key1 = prior tree.lkey1 
    and  tree.key2 = prior tree.lkey2 
    and  tree.key3 = prior tree.lkey3 
), 
maxTree as (
    select max(thisLevel) maxLevel 
    from subTree 
) 
select lkey1 key1, 
     lkey2 key2, 
     lkey3 key3, 
     1 - isLeaf option, 
     flag 

from subTree, 
     maxTree 
where (isLeaf = 1 or thisLevel = maxLevel - 1) 
and (length(key2) != length(lkey2) or substr(lkey2, 2, 1) != 'X'); 

Причина queryKeys потому, что он используется в другом месте в большем запросе и может содержать более одной записи. Часть maxTree является проблемой, за ее общей причудливостью!

Теперь причина для названия этой должности, потому что этот запрос может быть сделан много более простым, если я мог бы сослаться на FLAG поля родителя. Я пробовал подход JOIN к этой идее - присоединяясь к дереву с собой по соответствующим ключам, но, если я не ошибаюсь, это приведет к рекурсивной проблеме, при которой вам придется перебирать дерево, чтобы найти правильные родительские ключи (поскольку поля KEYx и LKEYx определяют полный составной ключ для записи).

(PS Использование Oracle 10gR2, если это имеет значение.)

ответ

3

Просто используйте:

PRIOR FLAG 

это даст вам именно то, что вы хотите - поле флаг родительской строки.

subTree as (   
select  tree.key1,    
     tree.key2,    
     tree.key3,    
     tree.lkey1,    
     tree.lkey2,    
     tree.lkey3,    
     tree.flag,    
     PRIOR TREE.FLAG PRIOR_FLAG 
     connect_by_isleaf isLeaf,    
     level thisLevel    

from  tree,    
     queryKeys 
(...) 
+0

Красивая! Спасибо :) – Xophmeister

1

я предполагал, ваше сообщение сводится к вопросу «Как я ссылаться на атрибут родительской строки FLAG в иерархическом запросе? "

Я не знаю, что SQL, с которым я пришел, является правильным. Прошу прощения, если это не так. В целом, однако, здесь был мой подход:

На каждом уровне в иерархии я объединил все ключи (SYS_CONNECT_BY_PATH). Затем, используя SUBSTR, INSTR и LEVEL, I SUBSTR, запишите, что составляет ключ родительского уровня. Наконец, в определении PARENT_FLAG, я выбираю FLAG строки, ключ которой соответствует этому ключу SUBSTR.

Установка:

CREATE TABLE tree (
    key1  VARCHAR2(5) 
, key2  VARCHAR2(10) 
, key3  VARCHAR2(5) 
, lkey1  VARCHAR2(5) 
, lkey2  VARCHAR2(10) 
, lkey3  VARCHAR2(5) 
, flag  VARCHAR2(1) 
); 
INSERT INTO tree VALUES ('09/10','10000','A1234','09/10','AU000123','A1234','1'); 
INSERT INTO tree VALUES ('09/10','10000','A1234','09/10','AU000456','A1234','1'); 
INSERT INTO tree VALUES ('09/10','10000','A1234','09/10','AX000001','A1234','1'); 
INSERT INTO tree VALUES ('09/10','AX000001','A1234','09/10','AE000010','A1234','0'); 
INSERT INTO tree VALUES ('09/10','AX000001','A1234','09/10','AE000020','A1234','0'); 
INSERT INTO tree VALUES ('09/10','AX000001','A1234','09/10','AE000030','A1234','0'); 
INSERT INTO tree VALUES ('09/10','10000','A1234','09/10','AX000002','A1234','0'); 
INSERT INTO tree VALUES ('09/10','AX000002','A1234','09/10','AE000040','A1234','0'); 
INSERT INTO tree VALUES ('09/10','10000','A1234','09/10','AU000789','A1234','0'); 

Запрос:

COL flag   FOR A4 
COL same_length FOR A11 
COL has_x_2nd  FOR A9 
COL full_key_path FOR A50 
COL parent_key FOR A30 
COL parent_flag FOR A11 
WITH querykeys AS (
    SELECT '09/10' key1 
    ,  '10000' key2 
    ,  'A1234' key3 
    FROM DUAL 
) 
, subtree1 AS (
    SELECT  tree.key1 
    ,   tree.key2 
    ,   tree.key3 
    ,   tree.lkey1 
    ,   tree.lkey2 
    ,   tree.lkey3 
    ,   tree.flag 
    ,   CONNECT_BY_ISLEAF isleaf 
    ,   LEVEL thislevel 
    ,   DECODE(LENGTH(tree.key2) 
      ,  LENGTH(tree.lkey2), '1' 
      ,  '0') same_length 
    ,   DECODE(UPPER(SUBSTR(tree.key2,2,1)) 
      ,  'X', '1' 
      ,  '0') has_x_2nd 
    ,   SYS_CONNECT_BY_PATH(tree.key1 || '|' || tree.key2 || '|' || tree.key3,'\') 
      || '\' 
      || tree.lkey1 || '|' || tree.lkey2 || '|' || tree.lkey3 || '\' full_key_path 
    FROM  tree 
    ,   querykeys 
    START WITH tree.key1 = querykeys.key1 
    AND  tree.key2 = querykeys.key2 
    AND  tree.key3 = querykeys.key3 
    CONNECT BY tree.key1 = PRIOR tree.lkey1 
    AND  tree.key2 = PRIOR tree.lkey2 
    AND  tree.key3 = PRIOR tree.lkey3 
) 
, subtree2 AS (
    SELECT st1.key1 
    ,  st1.key2 
    ,  st1.key3 
    ,  st1.lkey1 
    ,  st1.lkey2 
    ,  st1.lkey3 
    ,  st1.flag 
    ,  st1.isleaf 
    ,  st1.thislevel 
    ,  st1.same_length 
    ,  st1.has_x_2nd 
    ,  st1.full_key_path 
    ,  SUBSTR(st1.full_key_path 
      ,  INSTR(st1.full_key_path,'\',1,st1.thislevel) + 1 
      ,  INSTR(st1.full_key_path,'\',1,st1.thislevel + 1) 
        - INSTR(st1.full_key_path,'\',1,st1.thislevel) - 1) parent_key 
    FROM subtree1 st1 
) 
SELECT st2.key1 
,  st2.key2 
,  st2.key3 
,  st2.lkey1 
,  st2.lkey2 
,  st2.lkey3 
,  st2.flag 
,  st2.isleaf 
,  st2.thislevel 
,  st2.same_length 
,  st2.has_x_2nd 
,  (SELECT t_prime.flag 
     FROM tree t_prime 
     WHERE t_prime.key1 = SUBSTR(st2.parent_key 
           ,  1 
           ,  INSTR(st2.parent_key,'|',1,1) - 1) 
     AND t_prime.key2 = SUBSTR(st2.parent_key 
           ,  INSTR(st2.parent_key,'|',1,1) + 1 
           ,  INSTR(st2.parent_key,'|',1,2) 
            - INSTR(st2.parent_key,'|',1,1) - 1) 
     AND t_prime.key3 = SUBSTR(st2.parent_key 
           ,  INSTR(st2.parent_key,'|',1,2) + 1) 
     -- Following assumes all rows with parent keys have same flag value. 
     -- Avoids ORA-01427: single-row subquery returns more than one row. 
     AND ROWNUM = 1) parent_flag 
FROM subtree2 st2 
; 

Результат:

KEY1 KEY2  KEY3 LKEY1 LKEY2  LKEY3 FLAG  ISLEAF THISLEVEL SAME_LENGTH HAS_X_2ND PARENT_FLAG 
----- ---------- ----- ----- ---------- ----- ---- ---------- ---------- ----------- --------- ----------- 
09/10 10000  A1234 09/10 AU00A1234 1    1   1 0   0   1 
09/10 10000  A1234 09/10 AU000456 A1234 1    1   1 0   0   1 
09/10 10000  A1234 09/10 AU000789 A1234 0    1   1 0   0   1 
09/10 10000  A1234 09/10 AX000001 A1234 1    0   1 0   0   1 
09/10 AX000001 A1234 09/10 AE000010 A1234 0    1   2 1   1   0 
09/10 AX000001 A1234 09/10 AE000020 A1234 0    1   2 1   1   0 
09/10 AX000001 A1234 09/10 AE000030 A1234 0    1   2 1   1   0 
09/10 10000  A1234 09/10 AX000002 A1234 0    0   1 0   0   1 
09/10 AX000002 A1234 09/10 AE000040 A1234 0    1   2 1   1   0 

9 rows selected. 

SQL> 

Как я уже сказал, я не 100% уверен, что grokked модель данных полностью, но Надеюсь, ты сможешь следовать моему подходу.

+0

Я выбрал ответ @ arturro за его простоту перед разбором пути, но +1 за ваши усилия. Спасибо :) – Xophmeister

Смежные вопросы