Как запросить многоуровневую маркированную точку ххх в Oracle? - PullRequest
0 голосов
/ 27 сентября 2018

Типичные данные о сотрудниках 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 во вложенный запрос?Или запросить те же данные более элегантным способом?Похоже, один из распространенных сценариев для меня.

1 Ответ

0 голосов
/ 27 сентября 2018

Вы можете использовать два иерархических запроса, хотя, похоже, должен быть способ еще больше упростить это:

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 (что для 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 <> скрипка

...