【Oracle】【SQL】rownum, row_number() 和 rank()的区别

本文详细介绍了Oracle SQL中的ROWNUM、ROW_NUMBER()和RANK()的区别。ROWNUM表示数据读取顺序,常用于限制返回第一条数据;ROW_NUMBER()结合OVER子句可用于按指定条件分组和排序,返回行序列;RANK()与ROW_NUMBER()类似,但相同值会得到相同的排名。通过实例展示了这三者的用法和限制。

已知表结构如下:

SC表

sno

cnoscore
s001c00180
s001c00290
s002c00179
s002c00285
s003c00179
s003c00280

 

1. ROWNUM

1.1 rownum 是一个序列,是oracle数据库从数据文件或缓存中读取数据的顺序, 如:

select * from sc where rownum = 1

结果返回SC表的第一行数据

sno

cnoscore
s001c00180

 

1.2 select rownum是在查询结果中插入“伪列" 用于显示查询结果的序列,如:

select rownum, cno, score from sc where sno = 's001'

返回结果为:

ronum

cnoscore
1c00180
2c00290

 

1.3 当rownum和order by 在同一个SQL出现时,数据库将先对select结果插入rownum伪列在进行排序,如:

select rownum, cno, score from sc where sno = 's001' order by score DESC

返回结果为:

rownum

cnoscore
2c00290
1c00180

 

1.4 在where语句中使用rownum时,不能使用> 或 between,如:

select * from sc where rownum > 2;

select * from sc where rownum between 1 and 1;

以上语句均返回空值

 

 

2. ROW_NUMBER()

2.1 跟rownum 类似,row_number() 也是在查询结果中插入一个伪列,用以显示每一行的序列,但是row_number()不可单独使用,一般结合over(<window specification>)一起使用

如:

检索SC表中每科成绩前2名的同学:

select * from

(select sno, cno, score, row_number()over(partition by cno order by score DESC) rn

from sc)

where rn between 1 and 2

返回结果为:

snocnoscoreRN
s001c001801
s002c001792
s001c002901
s002c002852

在上述语句中 row_number()over(partition by cno order by score DESC) 的含义是:

partition by cno: 以column cno 进行分组

order by score DESC: 在分组完成的基础上,将每一组中的数据以column score 进行降序排列

row_number(): 在分组和排序完成的基础上,在结果表中插入一行伪劣,用以显示每一组的行序列。

 

 

3. RANK()

3.1 rank()跟row_number()的用法类似,通常跟over(partition by column A order by column B) 一起使用,区别是,row_number()返回结果不考虑列值相同的情况,而rank()返回列值相同的所有数据

如:

select * from

(select sno, cno, score, rank()over(partition by cno order by score DESC) rn

from sc)

where rn between 1 and 2

返回结果为:

snocnoscoreRN
s001c001801
s002c001792
s003c001792
s001c002901
s002c002852

使用row_number()时RN的值按1,2,3,4 顺序升序排列,不会重复

使用rank()时,对于值相同的行RN的值也相同,比如四行数据的第二行和第三行相同,那么RN的值是:1,2,2,4

 

<完>

<欢迎纠错/补充>

 

 

<think> 我们正在处理的是将包含ROW_NUMBER()函数的SQL语句转换为Oracle语法的问题。根据引用内容,Oracle支持ROW_NUMBER()函数,并且其用法与标准SQL基本一致。因此,如果原SQL是标准的使用ROW_NUMBER()的语句,那么在Oracle中通常可以直接使用,但需要注意Oracle特有的语法规则(如别名、子查询等)。 引用中提到了ROW_NUMBER()Oracle中的使用格式: ROW_NUMBER() OVER ([PARTITION BY col1, col2...] ORDER BY col3, col4...) 同时,引用[3][4]特别指出,ROW_NUMBER()ROWNUM伪列的区别在于:ROW_NUMBER()是先排序再生成序号,而ROWNUM是先生成序号再排序。因此,在分页等需要先排序再取序号的情况下,ROW_NUMBER()更合适。 如果用户给出的原SQL是其他数据库(如SQL Server)的ROW_NUMBER()用法,转换到Oracle时,通常只需要保持相同的结构,但需要注意: 1. Oracle要求OVER子句必须包含ORDER BY,而PARTITION BY是可选的。 2. 在子查询中使用ROW_NUMBER()时,Oracle要求为子查询指定别名。 由于用户没有提供具体的原SQL语句,我将给出一个通用的转换示例。 假设原SQLSQL Server)如下: SELECT *, ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNum FROM Orders WHERE CustomerID = 'VINET'; 转换为Oracle语法: SELECT o.*, ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNum FROM Orders o WHERE CustomerID = 'VINET'; 注意:在Oracle中,表别名可以不加AS(直接空格后跟别名),而列别名需要AS(但也可以省略AS,直接写别名)。但上面的例子中,由于ROW_NUMBER()的别名使用了AS,这是正确的。 如果原SQL使用了ROW_NUMBER()进行分页,那么通常需要嵌套子查询。例如,取第6到第10条记录: 原SQLSQL Server)可能这样写: SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNum FROM Orders ) AS T WHERE T.RowNum BETWEEN 6 AND 10; 在Oracle中,由于不允许在子查询中使用*(当有ROW_NUMBER()时,如果原表有重复列名,需要明确列出),但更常见的写法是: SELECT * FROM ( SELECT o.*, ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNum FROM Orders o ) T WHERE T.RowNum BETWEEN 6 AND 10; 注意:在Oracle中,子查询别名前的AS关键字要去掉(Oracle不允许在FROM子句的子查询别名前使用AS)。 另外,引用[3]给出了一个在Oracle中使用ROW_NUMBER()分页的例子: SELECT * FROM ( SELECT ROW_NUMBER() OVER(ORDER BY e.CREATE_TIME DESC) as rn, e.AAA as code, e.CREATE_TIME FROM HDD e ) t WHERE t.rn >= 1 AND t.rn <= 7; 因此,如果原SQL使用了类似的ROW_NUMBER()分页,转换时只需注意去掉子查询别名前的AS,并确保在Oracle中表名、列名等标识符都是有效的(比如大小写问题,Oracle默认是大写,如果原表名或列名是小写或有引号,需要保持一致)。 总结转换步骤: 1. 保留ROW_NUMBER()函数及其OVER子句,确保有ORDER BY。 2. 如果原语句中使用了表别名,在Oracle中仍然使用,但去掉表别名前的AS(列别名保留AS)。 3. 如果原语句使用了ROW_NUMBER()进行分页而使用了嵌套子查询,确保子查询没有使用AS(在子查询的括号后直接写别名)。 4. 注意在Oracle中,子查询中若使用了ROW_NUMBER(),则外层查询的列必须明确(如果内层用了*,那么外层可以用*,但要注意ROW_NUMBER()列别名的引用)。 如果用户提供了具体的原SQL,我们可以针对性地转换。但如果没有,以上是通用规则。 下面,我将按照用户要求生成相关问题。 </think> 以下是针对包含 `ROW_NUMBER()` 函数的 SQL 语句转换为 Oracle 语法的详细指南,结合 Oracle 特性与最佳实践: ### 转换核心原则 1. **基本语法保留** Oracle 的 `ROW_NUMBER()` 语法与标准 SQL 完全兼容,无需修改核心结构: ```sql ROW_NUMBER() OVER ([PARTITION BY col] ORDER BY col) ``` 2. **关键调整点** ```sql SELECT * FROM ( SELECT t.*, ROW_NUMBER() OVER (ORDER BY create_time DESC) AS rn -- ① 分析函数保持原样 FROM your_table t -- ② 表别名无需AS关键字 ) subquery_alias -- ③ 子查询别名直接写(去掉AS) WHERE rn BETWEEN 11 AND 20 -- ④ 直接引用别名过滤 ``` --- ### 典型场景转换示例 #### 场景 1:基础分页查询 ```sql /* 原始SQL */ SELECT * FROM ( SELECT *, ROW_NUMBER() OVER(ORDER BY id) AS rn FROM employees ) AS tmp WHERE rn BETWEEN 11 AND 20 /* Oracle 转换版 */ SELECT * FROM ( SELECT e.*, ROW_NUMBER() OVER (ORDER BY e.id) AS rn FROM employees e -- 表别名去AS ) tmp -- 子查询别名去AS WHERE tmp.rn BETWEEN 11 AND 20 ``` #### 场景 2:分区排序 ```sql /* 带 PARTITION BY 的分组排序 */ SELECT department_id, employee_name, salary, ROW_NUMBER() OVER ( PARTITION BY department_id ORDER BY salary DESC ) AS dept_rank FROM employees WHERE hire_date > DATE '2020-01-01' ``` --- ### 与 `ROWNUM` 伪列的对比 | 特性 | `ROW_NUMBER()` | `ROWNUM` | |--------------------|------------------------------------|---------------------------| | **生成时机** | 先排序后编号 [^4] | 先编号后排序 | | **分页可靠性** | ✅ 稳定 | ❌ 需嵌套子查询 | | **分区支持** | ✅ 通过 `PARTITION BY` | ❌ 不支持 | | **排序控制** | ✅ 显式定义 `ORDER BY` | ❌ 依赖主查询排序 | | **使用场景** | 分区排名、复杂分页 | 简单行号限制 | > **为什么优先选 `ROW_NUMBER()`?** > 当分页需要排序时: > ```sql > /* ROWNUM 错误示例 */ > SELECT * FROM employees > WHERE ROWNUM <= 10 > ORDER BY salary DESC; -- 先取前10行再排序,结果错误! > ``` --- ### 性能优化建议 1. **索引策略** 为 `ORDER BY` `PARTITION BY` 的列创建索引: ```sql CREATE INDEX idx_emp_dept_sal ON employees(department_id, salary DESC); ``` 2. **减少嵌套层数** 避免不必要的子查询: ```sql SELECT employee_id, ROW_NUMBER() OVER (ORDER BY salary) rn FROM employees WHERE rn <= 100; -- ❌ Oracle 不允许直接WHERE过滤分析函数结果 /* 正确写法 */ SELECT * FROM ( SELECT employee_id, ROW_NUMBER() OVER (ORDER BY salary) rn FROM employees ) WHERE rn <= 100; ``` 3. **并行处理 (10g+)** 大数据集启用并行查询: ```sql SELECT /*+ PARALLEL(8) */ ROW_NUMBER() OVER (ORDER BY sales_amount DESC) rn, product_id FROM sales_data ``` --- ### 常见错误解决 **ORA-30483: window functions not allowed here** ❌ 错误用法:在 `WHERE`/`GROUP BY` 中直接引用 `ROW_NUMBER()` ✅ 解决方案:嵌套子查询后在外部过滤 ```sql SELECT * FROM ( SELECT name, ROW_NUMBER() OVER (ORDER BY score DESC) rn FROM students ) WHERE rn = 1; -- 正确位置 ``` **ORA-00904: "RN": invalid identifier** ❌ 错误原因:子查询未返回 `rn` 列 ✅ 检查点: 1. 子查询中是否正确定义了别名 2. 外层是否使用了正确别名引用 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值