Oracle分析函数-使用FIRST_VALUE删除不需要的行
Oracle分析函数-使用FIRST_VALUE删除不需要的行
根据以下两个问题,我相信Oracle函数FIRST_VALUE是我需要使用的:
我有三个表示与组织关联的人员的表。每个组织可能有一个父组织,其中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);
这似乎类似于Pro Oracle SQL中的FIRST_VALUE示例,但无论我如何调整参数,都无法使其工作。
如何只返回给定组具有其最高级别值的行(即树中最远的位置)?
在使用Oracle分析函数中,出现了一个问题。问题的原因是在使用OVER (PARTITION BY ID,NAME ORDER BY lev DESC)
时,只应该对ID和NAME进行分区,而不应该包括path。此外,应该使用first_value(path)
而不是first_value(lev)
。
这个问题的解决方法是更改分区的设置,只对ID和NAME进行分区,不包括path。并且使用first_value(path)
来获取正确的值,而不是first_value(lev)
。
经过更改后,first_value(path)
列现在对于给定的ID的所有行都包含了正确的值,但是仍然存在重复的ID。
这就是你要求的。最初,我发布了一个删除重复行的查询,但一分钟后我将其删除了,因为我理解到你不想要那个。该查询与a_horse_with_no_name的查询相同。
原因:问题出现的原因是在使用Oracle分析函数FIRST_VALUE时,通过ROW_NUMBER函数对结果进行排序时,使用了错误的列名lev,导致结果不符合预期。
解决方法:将代码中的lev改为level,即order by level
。这样修改后,问题得到解决。以下是修改后的代码:
SELECT SELECT id, name, path FROM ( SELECT id, name, path, row_number() over (partition by id,name order by level 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
问题的出现原因是在使用Oracle分析函数时,需要使用FIRST_VALUE函数来删除不需要的行。解决方法是使用MAX函数结合KEEP子句来实现。
在给出的例子中,使用了MAX函数和KEEP子句来获取每个ID对应的最后一个NAME和PATH。具体代码如下:
SELECT id , max(name) keep (dense_rank last order by lev) name , max(path) keep (dense_rank last order by lev) path 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 ) group by id
通过以上代码,可以得到每个ID对应的最后一个NAME和PATH。结果如下:
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
以上就是使用FIRST_VALUE函数来删除不需要的行的解决方法。通过使用MAX函数和KEEP子句,可以得到每个ID对应的最后一个NAME和PATH。