【问题标题】:How to query multi level bullet point x.x.x in Oracle?如何在 Oracle 中查询多级项目符号点 x.x.x?
【发布时间】:2018-09-27 11:40:24
【问题描述】:

典型员工数据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 传递给嵌套查询? 或者以更优雅的方式查询相同的数据?对我来说,这似乎是一种常见的情况。

【问题讨论】:

  • 为什么要存储过程?您已经为 PL/SQL 添加了标记,表明您想要一个存储过程或函数作为答案。
  • 查询。允许使用 PL/SQL 函数。

标签: sql oracle plsql hierarchy


【解决方案1】:

您可以使用两个分层查询,但看起来应该有一种方法可以进一步简化它:

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 模式表给出:

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                         

db<>fiddle

【讨论】:

  • 递归 CTE,百万年之后都不会想到。
猜你喜欢
  • 2016-09-04
  • 2011-05-26
  • 1970-01-01
  • 2021-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-11
  • 2021-07-25
相关资源
最近更新 更多