为啥我们不能使用 rank() 分析函数来删除表中的重复项?

Posted

技术标签:

【中文标题】为啥我们不能使用 rank() 分析函数来删除表中的重复项?【英文标题】:Why cant we use rank() analytic function to delete duplicates in a table?为什么我们不能使用 rank() 分析函数来删除表中的重复项? 【发布时间】:2014-11-30 09:05:18 【问题描述】:

我创建了一个 emp 表,其中包含以下记录。

    create table emp(
    EMPNO  integer,
    EMPNAME varchar2(20),
    SALARY  number);

select * from emp;

empno empname  salary
10    bill     2000
11    bill     2000
12    mark     3000
12    mark     3000
12    mark     3000
12    philip   3000
12    john     3000
13    tom      4000
14    tom      4000
14    jerry    5000
14    matt     5000
15    susan    5000

为了删除重复项,我一直在使用 rownum() 函数以及 partition by 和 order by 子句,查询如下:

delete from emp where rowid in
(
select rid from
(
select rowid rid,
row_number() over(partition by empno order by empno) rn
from emp
)
where rn > 1
);
--6 rows deleted

该查询删除了所有具有重复 empno 的员工记录,结果如下所示:

empno empname  salary
10    bill     2000
11    bill     2000
12    mark     3000
13    tom      4000
14    tom      4000
15    susan    5000

当我使用内部查询来获取表中所有结果的行号时,它会给我以下结果:

select rowid as rid,empno,empname,
row_number() over(partition by empno order by empno) rn
from emp;

rowid                         rownumber
AACDJUAAPAAGLlTAAA  10  bill    1
AACDJUAAPAAGLlTAAB  11  bill    1
AACDJUAAPAAGLlTAAE  12  mark    1
AACDJUAAPAAGLlTAAD  12  mark    2
AACDJUAAPAAGLlTAAC  12  mark    3
AACDJUAAPAAGLlTAAF  12  philip  4
AACDJUAAPAAGLlTAAG  12  john    5
AACDJUAAPAAGLlTAAH  13  tom     1
AACDJUAAPAAGLlTAAI  14  tom     1
AACDJUAAPAAGLlTAAJ  14  jerry   2
AACDJUAAPAAGLlTAAK  14  matt    3
AACDJUAAPAAGLlTAAL  15  susan   1

但是当我使用 rank() 代替 rownumber() 函数时,它给了我以下结果:

select rowid as rid,empno,empname,
rank() over(partition by empno order by empno) rn
from emp;

rowid                          rank
AACDJUAAPAAGLlTAAA  10  bill    1
AACDJUAAPAAGLlTAAB  11  bill    1
AACDJUAAPAAGLlTAAE  12  mark    1
AACDJUAAPAAGLlTAAD  12  mark    1
AACDJUAAPAAGLlTAAC  12  mark    1
AACDJUAAPAAGLlTAAF  12  philip  1
AACDJUAAPAAGLlTAAG  12  john    1
AACDJUAAPAAGLlTAAH  13  tom     1
AACDJUAAPAAGLlTAAI  14  tom     1
AACDJUAAPAAGLlTAAJ  14  jerry   1
AACDJUAAPAAGLlTAAK  14  matt    1
AACDJUAAPAAGLlTAAL  15  susan   1

所以我的问题是,为什么 rank() 为表中的所有记录赋予相同的值,即使有重复的 empid?

【问题讨论】:

根据您的业务规则,"a duplicate" 到底是什么:(1) 相同 id (2) 相同名称 (3) 相同 id 同名? 【参考方案1】:

RANK() 就是这样工作的。为分区内的相同排名的行获得不同的RANK 值将是相当令人惊讶的。事实上,ORDER BY 子句是分区内RANK 的重要驱动因素,但是由于您对分区使用与排序相同的列,因此很明显,每一行在各自的分区中排名第一(因为它们是分区中唯一的值)

See an explanation in this blog post,这里的 SQL(PostgreSQL 语法)

SELECT
  v,
  ROW_NUMBER() OVER (window) row_number,
  RANK()       OVER (window) rank,
  DENSE_RANK() OVER (window) dense_rank
FROM t
WINDOW window AS (ORDER BY v)
ORDER BY v

...产生这个输出

+---+------------+------+------------+
| V | ROW_NUMBER | RANK | DENSE_RANK |
+---+------------+------+------------+
| a |          1 |    1 |          1 |
| a |          2 |    1 |          1 |
| a |          3 |    1 |          1 |
| b |          4 |    4 |          2 |
| c |          5 |    5 |          3 |
| c |          6 |    5 |          3 |
| d |          7 |    7 |          4 |
| e |          8 |    8 |          5 |
+---+------------+------+------------+

【讨论】:

知道了,但在我的示例中,为什么 rank() 没有增加?...我的意思是为什么它没有将 empno 11 的排名分配为“2”?...是因为我正在分区通过empno? @abhi1489 只有一个员工的empno=11,所以他在他的团队中排名第一。 @abhi1489,很抱歉,我没有从你的问题中听到那部分。是的,empno=11 在他们的分区中排名第一。你写查询的方式,其实每个人都在自己的分区中排名第一。【参考方案2】:

共有三个“排名”分析函数:row_number()rank()dense_rank()

这些都非常相似。他们按顺序为组内的行分配编号。该组由partition by 子句定义。排序由order by 子句定义。三者之间的区别在于它们处理重复值的方式。

row_number() always 返回组内的序号。当存在平局时,相等值的行具有顺序值,但它们是不同的。

dense_rank() 分配没有间隙的连续值。但是,相等值的行被赋予相同的值。下一个值的排名再高。

rank() 分配有间隔的连续值。等值的行具有相同的值,但后续行有一个间隙。

这是一个例子:

value  row_number   dense_rank     rank
  a        1            1           1
  b        2            2           2
  b        3            2           2
  b        4            2           2
  c        5            3           5
  d        6            4           6
  d        7            4           6

【讨论】:

以上是关于为啥我们不能使用 rank() 分析函数来删除表中的重复项?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle-分析函数_总结

[转]oracle分析函数Rank, Dense_rank, row_number

为啥使用 rank() 窗口函数会破坏解析器?

为啥我不能直接使用不在函数后面的另一个表中的列?

为啥我不能删除这个表格?

oracle的row_number()over rank()over和dense_rank()over这三种分析函数(转)