(CSDN博主:写代码也要符合基本法)
(图片自网络)
SQL> SELECT loc_code, loc_name, parent_code FROM demo_locations
2 /
LOC_CODE LOC_NAME PARENT_CODE
---------- ---------- -----------
100000 山东 0
200000 广东 0
110000 菏泽 100000
120000 青岛 100000
210000 佛山 200000
220000 深圳 200000
111000 曹县 110000
112000 单县 110000
113000 巨野 110000
9 rows selected
复制
说起来题目倒是简单,要求的展示结果也没有难以理解的地方。但越是这种看起来简单的问题,实现起来却弯弯绕绕的越多
SQL> SELECT t1.loc_name loc1, t2.loc_name loc2, t3.loc_name loc3
2 FROM demo_locations t1, demo_locations t2, demo_locations t3
3 WHERE t1.loc_code = t2.parent_code(+)
4 AND t2.loc_code = t3.parent_code(+)
5 AND t1.parent_code = '0'
6 /
LOC1 LOC2 LOC3
---------- ---------- ----------
山东 菏泽 曹县
山东 菏泽 单县
山东 菏泽 巨野
山东 青岛
广东 佛山
广东 深圳
6 rows selected
复制
虽然和最终结果还有差距,但已粗具规模,至少三列的层次已经尽数展现了
这当然是外连接本来的作用决定的:主表中的行即便关联不出外连接表里的数据,也要选择到结果集中。其隐含的意思是,关联不出的行都显示了,更别说能关联出来数据的行了,肯定是携手外连接表的数据一起显示了
说到这里,一定会有老爷说:做题就做题,你嘚嘚外连接干什么?你这就是在凑字数
GROUP BY t1.loc_name, ROLLUP(t2.loc_name, t3.loc_name)
复制
使用如上的分组规则,就能在不置空省列的前提下,多实现两种情形的分组:市列 + 置空的县列,以及置空的市列和县列

问题看起来是只有省和市的行重复出现了,原因也很好想到:佛山、深圳、青岛三市的行在原结果中就有,如今出现的两行分别是本来 LOC3 就为空的行(外连接所得)跟置空 LOC3 的行(分组所得)
SQL> SELECT t1.loc_name loc1, t2.loc_name loc2, t3.loc_name loc3
2 FROM demo_locations t1, demo_locations t2, demo_locations t3
3 WHERE t1.parent_code = '0'
4 AND t1.loc_code = t2.parent_code(+)
5 AND t2.loc_code = t3.parent_code(+)
6 GROUP BY t1.loc_name, ROLLUP(t2.loc_name, t3.loc_name)
7 HAVING grouping(t3.loc_name) = 1 OR t3.loc_name IS NOT NULL
8 ORDER BY loc1, loc2 NULLS FIRST, loc3 NULLS FIRST
9 /
LOC1 LOC2 LOC3
---------- ---------- ----------
广东
广东 佛山
广东 深圳
山东
山东 菏泽
山东 菏泽 曹县
山东 菏泽 单县
山东 菏泽 巨野
山东 青岛
9 rows selected
复制
效果还不错
查询路径?SYS_CONNECT_BY_PATH 一听:我四干介个的啊 → 传送门
SQL> WITH conn AS
2 (SELECT sys_connect_by_path(loc_name, '.') p
3 FROM demo_locations
4 CONNECT BY PRIOR loc_code = parent_code
5 START WITH parent_code = '0')
6 SELECT regexp_substr(p, '[^\.]+', 1, 1) loc1
7 ,regexp_substr(p, '[^\.]+', 1, 2) loc2
8 ,regexp_substr(p, '[^\.]+', 1, 3) loc3
9 FROM conn
10 /
LOC1 LOC2 LOC3
---------- ---------- ----------
山东
山东 菏泽
山东 菏泽 曹县
山东 菏泽 单县
山东 菏泽 巨野
山东 青岛
广东
广东 佛山
广东 深圳
9 rows selected
复制
SQL> WITH conn(lv, loc_code, loc1, loc2, loc3) AS
2 (SELECT 1 lv, loc_code, loc_name loc1, NULL loc2, NULL loc3
3 FROM demo_locations
4 WHERE parent_code = '0'
5 UNION ALL
6 SELECT c.lv + 1 lv
7 ,l.loc_code
8 ,c.loc1
9 ,decode(c.lv + 1, 2, l.loc_name, c.loc2) loc2
10 ,decode(c.lv + 1, 3, l.loc_name, c.loc3) loc3
11 FROM conn c, demo_locations l
12 WHERE c.loc_code = l.parent_code)
13 SELECT loc1, loc2, loc3 FROM conn ORDER BY loc_code
14 /
LOC1 LOC2 LOC3
---------- ---------- ----------
山东
山东 菏泽
山东 菏泽 曹县
山东 菏泽 单县
山东 菏泽 巨野
山东 青岛
广东
广东 佛山
广东 深圳
9 rows selected
复制
SQL> SELECT t1.loc_name loc1
2 ,t2.loc_name loc2
3 ,t3.loc_name loc3
4 ,t4.loc_name loc4
5 FROM demo_locations t1
6 ,demo_locations t2
7 ,demo_locations t3
8 ,demo_locations t4
9 WHERE t1.parent_code = '0'
10 AND t1.loc_code = t2.parent_code(+)
11 AND t2.loc_code = t3.parent_code(+)
12 AND t3.loc_code = t4.parent_code(+)
13 GROUP BY t1.loc_name, ROLLUP(t2.loc_name, t3.loc_name, t4.loc_name)
14 HAVING grouping_id(t3.loc_name, t4.loc_name) = 3 OR
15 grouping_id(t3.loc_name, t4.loc_name) = 1 AND t3.loc_name IS NOT NULL OR
16 t4.loc_name IS NOT NULL
17 ORDER BY loc1, loc2 NULLS FIRST, loc3 NULLS FIRST, loc4 NULLS FIRST
18 /
LOC1 LOC2 LOC3 LOC4
---------- ---------- ---------- ----------
广东
广东 佛山
广东 深圳
山东
山东 菏泽
山东 菏泽 曹县
山东 菏泽 曹县 牛逼
山东 菏泽 单县
山东 菏泽 巨野
山东 青岛
10 rows selected
SQL> WITH conn AS
2 (SELECT sys_connect_by_path(loc_name, '.') p
3 FROM demo_locations
4 CONNECT BY PRIOR loc_code = parent_code
5 START WITH parent_code = '0')
6 SELECT regexp_substr(p, '[^\.]+', 1, 1) loc1
7 ,regexp_substr(p, '[^\.]+', 1, 2) loc2
8 ,regexp_substr(p, '[^\.]+', 1, 3) loc3
9 ,regexp_substr(p, '[^\.]+', 1, 4) loc4
10 FROM conn
11 /
LOC1 LOC2 LOC3 LOC4
---------- ---------- ---------- ----------
山东
山东 菏泽
山东 菏泽 曹县
山东 菏泽 曹县 牛逼
山东 菏泽 单县
山东 菏泽 巨野
山东 青岛
广东
广东 佛山
广东 深圳
10 rows selected
SQL> WITH conn(lv, loc_code, loc1, loc2, loc3, loc4) AS
2 (SELECT 1 lv, loc_code, loc_name loc1, NULL loc2, NULL loc3, NULL loc4
3 FROM demo_locations
4 WHERE parent_code = '0'
5 UNION ALL
6 SELECT c.lv + 1 lv
7 ,l.loc_code
8 ,c.loc1
9 ,decode(c.lv + 1, 2, l.loc_name, c.loc2) loc2
10 ,decode(c.lv + 1, 3, l.loc_name, c.loc3) loc3
11 ,decode(c.lv + 1, 4, l.loc_name, c.loc4) loc4
12 FROM conn c, demo_locations l
13 WHERE c.loc_code = l.parent_code)
14 SELECT loc1, loc2, loc3, loc4 FROM conn ORDER BY loc_code
15 /
LOC1 LOC2 LOC3 LOC4
---------- ---------- ---------- ----------
山东
山东 菏泽
山东 菏泽 曹县
山东 菏泽 曹县 牛逼
山东 菏泽 单县
山东 菏泽 巨野
山东 青岛
广东
广东 佛山
广东 深圳
10 rows selected
复制



文章转载自SQL干货分享,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。