如何在Oracle中查询多级项目符号点x.x.x?
How to query multi level bullet point x.x.x in Oracle?
典型员工数据http://sqlfiddle.com/#!9/41d151/1/0作为示例数据
我需要得到这样的结果
+-----+-----------+-------+
| ID | NAME | rnum |
+-----+-----------+-------+
| 100 | King | 1 |
| 102 | De Haan | 1.1 |
| 103 | Hunold | 1.1.1 |
| 148 | Cambrault | 1.2 |
| 170 | Fox | 1.2.1 |
| 169 | Bloom | 1.2.3 |
| <...> |
| 147 | Errazuriz | 1.3 |
| 166 | Ande | 1.3.1 |
| 167 | Banda | 1.3.2 |
| <...>
| 521 | Smith | 5.14.3|
+-----+-----------+-------+
主要问题是rnum代。我无法将 e.level_ 传递给子查询。
WITH data AS (-- more useful employee list
SELECT id, name, level_order, level level_, manager_id, CONNECT_BY_ROOT id root_manager
FROM employee
CONNECT BY PRIOR employee_id = manager_id
START WITH manager_id IS null
ORDER BY level, level_order)
SELECT e.id, e.name, sys_connect_by_path((SELECT e2.rnum
FROM (-- employee at same level, sorted and numerated
SELECT row_number() OVER (ORDER BY t.level_order) rnum, t.employee_id
FROM data t
WHERE t.level_ = e.level_
ORDER BY t.level_order) e2
WHERE e2.employee_id = e.employee_id), '.')
FROM employee e
CONNECT BY PRIOR e.employee_id = e.manager_id
START WITH e.manager_id is null
/
有没有办法将 level 传递给嵌套查询?
或者以更优雅的方式查询相同的数据?看起来像是我的常见情况之一。
您可以使用两个分层查询,但看起来应该有一种方法可以进一步简化它:
select employee_id,
last_name,
ltrim(sys_connect_by_path(rn, '.'), '.') as rnum
from (
select employee_id, manager_id, last_name,
dense_rank() over (partition by level, manager_id order by last_name) as rn
from employees
start with manager_id is null
connect by manager_id = prior employee_id
)
start with manager_id is null
connect by manager_id = prior employee_id
order by employee_id;
其中包含您的 SQL Fiddle 中的 ID 子集(奇怪的是,这是 MySQL 而不是 Oracle 的)给出:
EMPLOYEE_ID LAST_NAME RNUM
----------- ------------------------- ------------------------------
100 King 1
102 De Haan 1.2
103 Hunold 1.2.1
104 Ernst 1.2.1.2
105 Austin 1.2.1.1
106 Pataballa 1.2.1.4
107 Lorentz 1.2.1.3
147 Errazuriz 1.3
148 Cambrault 1.1
166 Ande 1.3.1
167 Banda 1.3.2
168 Ozer 1.1.5
169 Bloom 1.1.2
170 Fox 1.1.3
171 Smith 1.1.6
172 Bates 1.1.1
173 Kumar 1.1.4
或使用完整的默认 HR 模式 table 给出:
EMPLOYEE_ID LAST_NAME RNUM
----------- ------------------------- ------------------------------
100 King 1
101 Kochhar 1.7
102 De Haan 1.2
103 Hunold 1.2.1
104 Ernst 1.2.1.2
105 Austin 1.2.1.1
106 Pataballa 1.2.1.4
107 Lorentz 1.2.1.3
108 Greenberg 1.7.2
109 Faviet 1.7.2.2
110 Chen 1.7.2.1
...
204 Baer 1.7.1
205 Higgins 1.7.3
206 Gietz 1.7.3.1
107 rows selected.
无论哪种方式,它都不完全符合您建议的结果;例如,只有一位顶级经理,而不是您的示例输出所期望的五位。
如果您使用的是 11gR2,则可以改用递归 CTE,这可能更容易理解并且(无论如何对我而言)更直观:
with rcte (employee_id, last_name, rnum) as (
select e.employee_id, e.last_name,
to_char(dense_rank() over (order by e.last_name))
from employees e
where manager_id is null
union all
select e.employee_id, e.last_name,
r.rnum ||'.'|| dense_rank() over (partition by r.rnum order by e.last_name)
from rcte r
join employees e on e.manager_id = r.employee_id
)
select *
from rcte
order by employee_id;
得到相同的结果。
我猜你是按姓氏顺序处理每个级别的,但如果这不是你真正想要的,你可以调整。
如果您想按这些级别对结果进行排序,那么一种蛮力方法是对生成的 rnum
值进行标记化:
...
order by
to_number(regexp_substr(rnum, '[^.]+', 1, 1)) nulls first,
to_number(regexp_substr(rnum, '[^.]+', 1, 2)) nulls first,
to_number(regexp_substr(rnum, '[^.]+', 1, 3)) nulls first,
to_number(regexp_substr(rnum, '[^.]+', 1, 4)) nulls first;
再次使用较小的子集,现在给出:
EMPLOYEE_ID LAST_NAME RNUM
----------- ------------------------- ------------------------------
100 King 1
148 Cambrault 1.1
172 Bates 1.1.1
169 Bloom 1.1.2
170 Fox 1.1.3
173 Kumar 1.1.4
168 Ozer 1.1.5
171 Smith 1.1.6
102 De Haan 1.2
103 Hunold 1.2.1
105 Austin 1.2.1.1
104 Ernst 1.2.1.2
107 Lorentz 1.2.1.3
106 Pataballa 1.2.1.4
147 Errazuriz 1.3
166 Ande 1.3.1
167 Banda 1.3.2
典型员工数据http://sqlfiddle.com/#!9/41d151/1/0作为示例数据
我需要得到这样的结果
+-----+-----------+-------+
| ID | NAME | rnum |
+-----+-----------+-------+
| 100 | King | 1 |
| 102 | De Haan | 1.1 |
| 103 | Hunold | 1.1.1 |
| 148 | Cambrault | 1.2 |
| 170 | Fox | 1.2.1 |
| 169 | Bloom | 1.2.3 |
| <...> |
| 147 | Errazuriz | 1.3 |
| 166 | Ande | 1.3.1 |
| 167 | Banda | 1.3.2 |
| <...>
| 521 | Smith | 5.14.3|
+-----+-----------+-------+
主要问题是rnum代。我无法将 e.level_ 传递给子查询。
WITH data AS (-- more useful employee list
SELECT id, name, level_order, level level_, manager_id, CONNECT_BY_ROOT id root_manager
FROM employee
CONNECT BY PRIOR employee_id = manager_id
START WITH manager_id IS null
ORDER BY level, level_order)
SELECT e.id, e.name, sys_connect_by_path((SELECT e2.rnum
FROM (-- employee at same level, sorted and numerated
SELECT row_number() OVER (ORDER BY t.level_order) rnum, t.employee_id
FROM data t
WHERE t.level_ = e.level_
ORDER BY t.level_order) e2
WHERE e2.employee_id = e.employee_id), '.')
FROM employee e
CONNECT BY PRIOR e.employee_id = e.manager_id
START WITH e.manager_id is null
/
有没有办法将 level 传递给嵌套查询? 或者以更优雅的方式查询相同的数据?看起来像是我的常见情况之一。
您可以使用两个分层查询,但看起来应该有一种方法可以进一步简化它:
select employee_id,
last_name,
ltrim(sys_connect_by_path(rn, '.'), '.') as rnum
from (
select employee_id, manager_id, last_name,
dense_rank() over (partition by level, manager_id order by last_name) as rn
from employees
start with manager_id is null
connect by manager_id = prior employee_id
)
start with manager_id is null
connect by manager_id = prior employee_id
order by employee_id;
其中包含您的 SQL Fiddle 中的 ID 子集(奇怪的是,这是 MySQL 而不是 Oracle 的)给出:
EMPLOYEE_ID LAST_NAME RNUM
----------- ------------------------- ------------------------------
100 King 1
102 De Haan 1.2
103 Hunold 1.2.1
104 Ernst 1.2.1.2
105 Austin 1.2.1.1
106 Pataballa 1.2.1.4
107 Lorentz 1.2.1.3
147 Errazuriz 1.3
148 Cambrault 1.1
166 Ande 1.3.1
167 Banda 1.3.2
168 Ozer 1.1.5
169 Bloom 1.1.2
170 Fox 1.1.3
171 Smith 1.1.6
172 Bates 1.1.1
173 Kumar 1.1.4
或使用完整的默认 HR 模式 table 给出:
EMPLOYEE_ID LAST_NAME RNUM
----------- ------------------------- ------------------------------
100 King 1
101 Kochhar 1.7
102 De Haan 1.2
103 Hunold 1.2.1
104 Ernst 1.2.1.2
105 Austin 1.2.1.1
106 Pataballa 1.2.1.4
107 Lorentz 1.2.1.3
108 Greenberg 1.7.2
109 Faviet 1.7.2.2
110 Chen 1.7.2.1
...
204 Baer 1.7.1
205 Higgins 1.7.3
206 Gietz 1.7.3.1
107 rows selected.
无论哪种方式,它都不完全符合您建议的结果;例如,只有一位顶级经理,而不是您的示例输出所期望的五位。
如果您使用的是 11gR2,则可以改用递归 CTE,这可能更容易理解并且(无论如何对我而言)更直观:
with rcte (employee_id, last_name, rnum) as (
select e.employee_id, e.last_name,
to_char(dense_rank() over (order by e.last_name))
from employees e
where manager_id is null
union all
select e.employee_id, e.last_name,
r.rnum ||'.'|| dense_rank() over (partition by r.rnum order by e.last_name)
from rcte r
join employees e on e.manager_id = r.employee_id
)
select *
from rcte
order by employee_id;
得到相同的结果。
我猜你是按姓氏顺序处理每个级别的,但如果这不是你真正想要的,你可以调整。
如果您想按这些级别对结果进行排序,那么一种蛮力方法是对生成的 rnum
值进行标记化:
...
order by
to_number(regexp_substr(rnum, '[^.]+', 1, 1)) nulls first,
to_number(regexp_substr(rnum, '[^.]+', 1, 2)) nulls first,
to_number(regexp_substr(rnum, '[^.]+', 1, 3)) nulls first,
to_number(regexp_substr(rnum, '[^.]+', 1, 4)) nulls first;
再次使用较小的子集,现在给出:
EMPLOYEE_ID LAST_NAME RNUM
----------- ------------------------- ------------------------------
100 King 1
148 Cambrault 1.1
172 Bates 1.1.1
169 Bloom 1.1.2
170 Fox 1.1.3
173 Kumar 1.1.4
168 Ozer 1.1.5
171 Smith 1.1.6
102 De Haan 1.2
103 Hunold 1.2.1
105 Austin 1.2.1.1
104 Ernst 1.2.1.2
107 Lorentz 1.2.1.3
106 Pataballa 1.2.1.4
147 Errazuriz 1.3
166 Ande 1.3.1
167 Banda 1.3.2