Аналитическая функция Oracle - использование FIRST_VALUE для удаления нежелательных строк - PullRequest
4 голосов
/ 28 января 2012

Я считаю, что функция Oracle FIRST_VALUE - это то, что мне нужно использовать, основываясь на этих двух вопросах: SQL - Как выбрать строку, имеющую столбец с максимальным значением
Oracle: получение записи с максимальной датой

У меня есть 3 таблицы, которые представляют людей, связанных с организациями. У каждой организации может быть родительский орган, где ORG.PARENT является внешним ключом для ORG.ID (поэтому таблица ссылается на себя). Человек может быть связан с более чем одной группой.

PERSON

ID    NAME
----------
1     Bob

ORG

ID    NAME        PARENT
------------------------
1     A           (null)
2     A-1              1
3     A-2              1
4     A-3              1
5     A-1-a            2
6     A-1-b            2
7     A-2-a            3
8     A-2-b            3

PERSON_TO_ORG

PERSON_ID  ORG_ID
-----------------
    1        1
    1        3

Я хочу перечислить группы, с которыми связан человек, поэтому я использовал этот запрос:

SELECT NAME, ID, sys_connect_by_path(NAME, '/') AS path
FROM org
START WITH ID IN
(SELECT org_id FROM person_to_org WHERE person_id=1)
connect by prior org.ID = org.parent;

... что дает мне:

NAME    ID    PATH
------------------
A-2     3     /A-2
A-2-a   8     /A-2/A-2-a
A-2-b   9     /A-2/A-2-b
A       1     /A
A-1     2     /A/A-1
A-1-a   5     /A/A-1/A-1-a
A-1-b   6     /A/A-1/A-1-b
A-2     3     /A/A-2
A-2-a   8     /A/A-2/A-2-a
A-2-b   9     /A/A-2/A-2-b
A-3     4     /A/A-3

Обратите внимание, как A-2 появляется дважды, как и должно быть. Однако я не хочу, чтобы группа появлялась дважды. Я хочу, чтобы группа отображалась только на самом низком уровне в дереве, то есть на самом высоком уровне. Вот как я безуспешно пытался использовать FIRST_VALUE - я все еще получаю A-2 (и другие), появляющиеся дважды:

SELECT id, name, path, first_value(lev) OVER
(
PARTITION BY ID,NAME, path ORDER BY lev DESC
) AS max_lev FROM
(SELECT NAME, ID, sys_connect_by_path(NAME, '/') AS path, LEVEL as lev
FROM org START WITH ID IN
(SELECT org_id FROM person_to_org WHERE person_id=1)
connect by prior org.ID = org.parent);

Это похоже на пример FIRST_VALUE в Pro Oracle SQL, но я не могу заставить его работать независимо от того, как я настраиваю параметры.

Как я могу вернуть только те строки, в которых данная группа имеет самое высокое значение уровня (т. Е. Самое дальнее в дереве)?

Ответы [ 3 ]

3 голосов
/ 28 января 2012

Как уже говорилось в одном из потоков, на которые вы ссылаетесь, аналитика - не самый эффективный способ: вам нужно объединиться для фильтрации дубликатов.

SQL> SELECT id
  2       , max(name) keep (dense_rank last order by lev) name
  3       , max(path) keep (dense_rank last order by lev) path
  4    FROM ( SELECT NAME
  5                , ID
  6                , sys_connect_by_path(NAME, '/') AS path
  7                , LEVEL as lev
  8             FROM org
  9            START WITH ID IN (SELECT org_id FROM person_to_org WHERE person_id=1)
 10          connect by prior org.ID = org.parent
 11         )
 12   group by id
 13  /

        ID NAME  PATH
---------- ----- --------------------
         1 A     /A
         2 A-1   /A/A-1
         3 A-2   /A/A-2
         4 A-3   /A/A-3
         5 A-1-a /A/A-1/A-1-a
         6 A-1-b /A/A-1/A-1-b
         7 A-2-a /A/A-2/A-2-a
         8 A-2-b /A/A-2/A-2-b

8 rows selected.

С уважением,
Роб.

PS: Вот еще немного информации о агрегатной функции LAST: http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions071.htm#sthref1495

2 голосов
/ 28 января 2012

Что насчёт этого (не проверено)

SELECT 
    SELECT id, 
           name, 
           path
FROM (            
    SELECT id, 
           name, 
           path, 
           row_number() over (partition by id,name order by lev desc) as rn
    FROM (
       SELECT NAME, 
              ID, 
              sys_connect_by_path(NAME, '/') AS path, 
              LEVEL as lev
       FROM org 
       START WITH ID IN (SELECT org_id FROM person_to_org WHERE person_id=1)
       connect by prior org.ID = org.parent
    )
) 
where rn = 1
1 голос
/ 28 января 2012

Вы должны разделить только OVER (PARTITION BY ID,NAME ORDER BY lev DESC) не ID,NAME, path

Редактировать: И, вероятно, вы хотите first_value(path), а не first_value(lev)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...