我一直在努力解决如何应对这种情况:
我的表格结构如下:
Family_code | Parent_Family_Code | ....
1 2
2 4
3 6
4 3
......................
当用户搜索特定的家庭代码时,我需要返回整个路径(最多10个级别),例如,对于family_code = 1,我需要:
Family_code | parent_1 | p_2 | p_3 | p_4 | p_5 | .....
1 2 4 3 6 null null.....
我知道我可以使用sys_connect_by_path()
,这会给我带来预期的结果但是作为一个字符串,而不是单独的列,这是我宁愿避免的。
这也可以通过10个左连接到同一个表来完成,或者使用LEAD()/LAG()
函数,这些函数将包含大量子查询,并且会产生一个混乱且不可读的查询,但话又说回来,它应该更重,我需要尽可能地简化它。
我使用substr()
函数提出解决方案(代码长度始终为varchar2(3)):
SELECT s.family_code,
s.parent_family_code_1,
s.parent_family_code_2,
CASE WHEN length(s.family_path) - (4 * 3 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 3 + 2), 3) ELSE NULL END as parent_family_code_3,
CASE WHEN length(s.family_path) - (4 * 4 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 4 + 2), 3) ELSE NULL END as parent_family_code_4,
CASE WHEN length(s.family_path) - (4 * 5 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 5 + 2), 3) ELSE NULL END as parent_family_code_5,
CASE WHEN length(s.family_path) - (4 * 6 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 6 + 2), 3) ELSE NULL END as parent_family_code_6,
CASE WHEN length(s.family_path) - (4 * 7 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 7 + 2), 3) ELSE NULL END as parent_family_code_7,
CASE WHEN length(s.family_path) - (4 * 8 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 8 + 2), 3) ELSE NULL END as parent_family_code_8,
CASE WHEN length(s.family_path) - (4 * 9 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 9 + 2), 3) ELSE NULL END as parent_family_code_9,
CASE WHEN length(s.family_path) - (4 * 10 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 10 + 2), 3) ELSE NULL END as parent_family_code_10
FROM (SELECT t.family_code,
t.parent_family_code as parent_family_code_1,
prior t.parent_family_code as parent_family_code_2,
sys_connect_by_path(t.family_code, ',') as family_path
FROM table t
connect by prior t.family_code = t.parent_family_code) s
但我想要一个不使用子串的解决方案,因为当其他开发人员触摸它时,很难对其进行任何维护 。 所以基本上我的问题是 - 如何在不使用子串的情况下选择整个路径作为不同的列?
答案 0 :(得分:3)
Oracle安装程序:
CREATE TABLE table_name ( Family_code, Parent_Family_Code ) AS
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 3, 6 FROM DUAL UNION ALL
SELECT 6, NULL FROM DUAL UNION ALL
SELECT 4, 3 FROM DUAL UNION ALL
SELECT 4, 5 FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL UNION ALL
SELECT 8, 7 FROM DUAL UNION ALL
SELECT 7, 9 FROM DUAL UNION ALL
SELECT 9, 10 FROM DUAL UNION ALL
SELECT 10, 11 FROM DUAL UNION ALL
SELECT 11, NULL FROM DUAL;
<强>查询强>:
SELECT TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth, NULL, 1 ) ) AS family_code,
CASE WHEN max_depth > 1 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 1, NULL, 1 ) ) END AS p1,
CASE WHEN max_depth > 2 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 2, NULL, 1 ) ) END AS p2,
CASE WHEN max_depth > 3 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 3, NULL, 1 ) ) END AS p3,
CASE WHEN max_depth > 4 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 4, NULL, 1 ) ) END AS p4,
CASE WHEN max_depth > 5 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 5, NULL, 1 ) ) END AS p5,
CASE WHEN max_depth > 6 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 6, NULL, 1 ) ) END AS p6,
CASE WHEN max_depth > 7 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 7, NULL, 1 ) ) END AS p7,
CASE WHEN max_depth > 8 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 8, NULL, 1 ) ) END AS p8,
CASE WHEN max_depth > 9 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 9, NULL, 1 ) ) END AS p9,
CASE WHEN max_depth > 10 THEN TO_NUMBER( REGEXP_SUBSTR( path, '/(\d+)', 1, max_depth - 10, NULL, 1 ) ) END AS p10
FROM (
SELECT SYS_CONNECT_BY_PATH( Family_code, '/' ) AS path,
LEVEL AS max_depth
FROM table_name
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR Family_Code = Parent_Family_Code
START WITH Parent_Family_Code IS NULL
);
<强>输出强>:
FAMILY_CODE P1 P2 P3 P4 P5 P6 P7 P8 P9 P10
----------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 2 4 5
1 2 4 3 6
8 7 9 10 11
答案 1 :(得分:0)
使用PIVOT
子句从@ MT0回答稍微修改过的查询。
SELECT *
FROM (
select connect_by_root( family_code ) as Family_code,
'P_' || level lev_el,
parent_family_code
from table_name t
start with not exists(
select 1 from table_name t1
where t.family_code = t1.parent_family_code )
connect by prior parent_family_code = family_code
)
PIVOT (
max( parent_family_code )
FOR (lev_el) IN (
'P_1', 'P_2', 'P_3', 'P_4', 'P_5', 'P_6','P_7', 'P_8','P_9','P_10' ,
'P_11', 'P_12', 'P_13', 'P_14', 'P_15', 'P_16','P_17', 'P_18','P_19','P_20',
'P_21', 'P_22', 'P_23', 'P_24', 'P_25', 'P_26','P_27', 'P_28','P_29','P_30'
/* add more "levels" here if required */
)
);
从@ MT0回答查询样本数据的结果(@ MT0,感谢您提供样本数据):
FAMILY_CODE 'P_1' 'P_2' 'P_3' 'P_4' 'P_5' 'P_6' 'P_7' 'P_8' 'P_9' 'P_10' 'P_11' 'P_12' 'P_13' 'P_14' 'P_15' 'P_16' 'P_17' 'P_18' 'P_19' 'P_20' 'P_21' 'P_22' 'P_23' 'P_24' 'P_25' 'P_26' 'P_27' 'P_28' 'P_29' 'P_30'
--------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 2 4 5 6
8 7 9 10 11