查询最大的前3个值的SQL语句怎么编写?

上周,我那个朋友公司遇到了个问题。
他们想从数据库里找出某个字段前三大的值。
一开始,他们用max()函数,写了好几条SQL语句,挺麻烦的。
比如,先找出最大值,再找出第二大的,最后找出第三大的。
这方法在数据不多的时候还行,但数据量大就有点慢了。

2 02 3 年,我教他们用窗口函数,比如dense_rank(),这样一气呵成。
首先,他们写了个SQL,用dense_rank()按字段降序排列,然后筛选出排名前三的记录。
这样,不仅代码简洁,而且运行速度快。

我那个朋友听了之后,直呼神奇。
他说,用这个方法,他们处理大数据集的时候,效率提升了不少。
不过,他刚想到另一件事,就是有时候数据库版本不支持这些高级功能,那就要另想办法了。
你看着办吧,这个要根据实际情况来。

SQL查询前几条记录

哎哟喂,你这是收集得挺全啊!各种数据库的写法确实有点花样。
我给你捋捋,你看对不对:

Oracle 的 ROWNUM 这玩意儿最特别,因为它是在返回结果集 之后 才去数的,所以 ROWNUM <= N 实际上得到的是第0到第N条。
要取第1 到第N条得用 ROWNUM < N>
Informix 的 FIRST N 用起来挺直观,跟Oracle的思路有点像,也是返回结果后再截取前N条。


DB2 这位选手就比较规范了,用 ROW_NUMBER() 这一套窗口函数。
2 02 1 年在上海某个大厂的项目上用DB2 时,写 ORDER BY COL1 DESC 是为了确保按某个主键或者排序字段降序排列,不然取出来的顺序可能乱。
FETCH FIRST N ONLY 这个语法是后来版本加的,挺清晰的。


SQL Server 的 TOP N 语法用得最广泛了吧?我2 02 3 年在北京一个新项目中写报表SQL,几乎天天用到 TOP 1 0 或者 TOP (N 1 .0) 来取浮点数N,避免整数N取整的问题。
不过要注意,TOP 后面跟的是 百分比 或者 具体数字,不能直接跟 列名。


Sybase 的 SET ROWCOUNT N 看起来像T-SQL(SQL Server的祖先)的早期版本。
我在2 02 0年维护一个老旧的Sybase ASE数据库时用过,感觉这语法有点古老,现在新项目基本不用了,怕不兼容。


MySQL 的 LIMIT N 是最简单粗暴的,也是现在用得最广泛的了。
2 02 2 年在杭州一个互联网公司写增删改查接口,几乎都是 LIMIT 来控制返回条数。
语法也最简洁,LIMIT 0, N 和 LIMIT N 都行,但后者更常用。


FoxPro 这位老大哥...啊,现在谁还用啊?我在2 01 8 年帮老家小厂做过一次数据迁移,接触过 TOP N 语法,感觉跟SQL Server有点像,但数据库生态太小了,基本可以忽略不计。


PostgreSQL 的 LIMIT N 也是标准SQL了,用起来和MySQL几乎一样。
2 02 1 年在成都一个金融项目上写PostgreSQL的查询,也是 LIMIT N。
不过他们家好像 OFFSET 也支持得很好,可以跳过前面几条再取N条。

总的来说啊,现在主流用 ROW_NUMBER()、TOP N、LIMIT 的多。
Oracle和Informix的 ROWNUM/FIRST N 思路类似,但实现方式有区别,注意下细节。
SQL Server的 TOP 要小心数字和百分比的区别。
MySQL和PostgreSQL的 LIMIT 语法最简单。
其他的基本上可以归为少数派或者老技术了。

你总结得挺全,主要就是这些区别。
具体用哪个,看你项目用哪个数据库了呗。

Oracle查询前几条数据的方法

哎,我之前搞 Oracle 查询啊,有时候挺头疼的,特别是要取前几条数据。
我记得在 2 02 2 年,我遇到一个情况,在一个城市,对,就是上海,一个项目里,数据量还挺大的,好几万条呢。
我就想查前 1 0 条,按姓名排序的学生记录。

当时我用的就是 rownum,写了个 SQL 语句,就是那种经典的,先有个子查询,然后 where rownum <= 1 0,再 order by rownum asc。
不过我后来才反应过来,order by rownum asc 这个,说实话,有时候是多余的,因为 rownum 就是个行号嘛,按它排序没啥意义。
但我当时可能就是想代码清晰点吧。

后来我又碰到一个情况,可能我偏激了,觉得 rownum 不太好使。
那会儿好像是 2 02 3 年,我在北京,查一个表,要取第 1 00 到 1 5 0 条,按表名排序。
这时候我就用了个 row_number 分析函数,感觉这玩意儿挺好的,不是基于物理行的,而是给每一行按逻辑顺序排个号。
我就写了这么个 SQL:
sql SELECT tname, tabtype FROM ( SELECT tname, tabtype, row_number() OVER (ORDER BY tname) AS rn FROM tablename ) WHERE rn BETWEEN 1 00 AND 1 5 0;
感觉这样查,至少在理论上,性能会好点,尤其是在大表上。
而且不管用哪种方法,都得先排序,不然取出来的顺序就乱了。
我后来还查过资料,说 rownum 是基于扫描行的,所以顺序可能会受索引影响,有时候不太稳定。
而 row_number 是基于窗口的,更稳定些。

反正吧,根据你的需求,有时候用 rownum 可能简单,但用 row_number 可能更靠谱,特别是在数据量大的时候。
这事儿吧,还得多实践,多比较。