首页 游戏攻略 正文

sqlserverrownum取前N行数据,简单几招快速实现!

兄弟们,今天来聊聊我在SQL Server里怎么搞定“取前N行数据”这事儿。说起来,我最开始接触数据库那会儿,都是跟Oracle打交道,所以脑子里一蹦出来就是个“ROWNUM”。那玩意儿多好使,唰唰两下就把前N行数据给撸出来了。

本站为89游戏官网游戏攻略分站,89游戏每日更新热门游戏,下载请前往主站地址:www.gm89.icu

可等我跳槽到了现在这家公司,他们后端用的清一色都是SQL Server。我琢磨着,这不也差不多嘛数据库嘛大同小异。结果,接了个活儿,要从一个大表里,按照某个条件排序,然后只取最前面那几条数据。我想都没想,直接上手就写了个:

SELECT ROWNUM, FROM 我的大表 ORDER BY 某个字段 DESC WHERE ROWNUM <= 10;

结果?啪!一个红叉叉,SQL Server直接给我报错了。我当时就懵圈了,心想,不是,这ROWNUM怎么就不能用了?是不是我哪里写错了?反复看了好几遍,语法没毛病。我那时候真是抓耳挠腮,茶不思饭不想,就为了这么个破问题。

后来没办法,就去查资料了。一查才知道,感情SQL Server里压根儿就没有“ROWNUM”这玩意儿!这是Oracle特有的。那一刻我感觉自己像个傻子,白忙活了半天。不过也多亏了那次,才让我知道了SQL Server有它自己的一套玩法。

第一招:简单粗暴的TOP关键字

查资料查着查着,就看到了SQL Server的TOP关键字。这玩意儿一瞅,这不就是ROWNUM的SQL Server版嘛用法还挺直接的,就是要在SELECT后面直接跟着TOP N,或者TOP N PERCENT

比如,我要取前10条数据,直接这样写:

SELECT TOP 10 FROM 我的大表 ORDER BY 某个字段 DESC;

一下就成了!当时那叫一个高兴,感觉跟打通了任督二脉似的。这招是真的好用,平时遇到什么需要取前几名、前几条数据这种简单需求,用TOP就完事儿了,效率也挺高。我记得有一次,老板急着要看上个月销售额最高的前五名客户,我用这个TOP 5一跑,数据立马就出来了,老板还夸我效率高,心里美滋滋。

后来还发现,TOP后面还能跟PERCENT,就是取百分之多少的数据。比如取前10%的记录:

SELECT TOP 10 PERCENT FROM 我的大表 ORDER BY 某个字段 DESC;

这玩意儿在需要做一些数据抽样或者比例分析的时候特别方便。不过TOP关键字有个小坑,就是它默认是不考虑并列关系的。比如,如果有两个人分数一样都是第一名,但你只取TOP 1,它可能只会给你其中一个,另一个就被忽略了。这在有些严格的排名场景下就不太行了。

第二招:灵活性更高的窗口函数ROW_NUMBER()

用了一段时间TOP之后,我遇到了一个稍微复杂点的需求。当时要做一个榜单,要取每个城市销售额前三的商品,而且要严格考虑并列的情况,也就是如果并列第三,那也得都显示出来。这下TOP就不太好使了。

我又开始捣鼓了,这回学聪明了,直接奔着“SQL Server 排名”关键词去搜。结果发现了“窗口函数”这玩意儿,其中就有一个叫ROW_NUMBER()的。这个函数可就厉害了,它能给你的查询结果集中的每一行都分配一个唯一的序号。

怎么用?它得配合OVER()子句一起用,在OVER()里面指定怎么分组(PARTITION BY)和怎么排序(ORDER BY)。

比如,我要给我的大表里的数据按某个字段排序,然后给每一行编号,我可以这么写:

SELECT , ROW_NUMBER() OVER (ORDER BY 某个字段 DESC) AS 行号 FROM 我的大表;

这样,每一行就都有个唯一的“行号”了。那怎么取前N行?简单,把这个查询当成一个子查询或者放到一个CTE(公共表表达式)里,然后外面再加个WHERE 行号 <= N不就行了!

具体的例子,比如取整个表的前10行:

WITH CTE_我的数据 AS (
    SELECT , ROW_NUMBER() OVER (ORDER BY 某个字段 DESC) AS 行号
    FROM 我的大表

SELECT FROM CTE_我的数据 WHERE 行号 <= 10;

这招跟TOP实现的效果一样,但它的强大之处在于可以PARTITION BY。比如说,我要取每个城市销量前三的商品,就可以这样搞:

WITH CTE_城市商品排名 AS (
    SELECT
        商品名,
        城市,
        销售额,
        ROW_NUMBER() OVER (PARTITION BY 城市 ORDER BY 销售额 DESC) AS 城市内排名
    FROM 销售明细表

SELECT FROM CTE_城市商品排名 WHERE 城市内排名 <= 3;

看到没,一个PARTITION BY 城市就搞定了按城市分组排名!这一下就解决了我那个复杂需求,而且排位次序非常精确。这让我尝到了窗口函数的甜头,感觉SQL Server的世界一下子开阔了好多。后来我还琢磨了一下,ROW_NUMBER()是给每一行都一个不重复的序号,即使有并列的值,序号也会接着往下排。如果真要考虑并列,让并列的行有相同的序号,那就可以用RANK()或者DENSE_RANK(),它们俩的区别在于RANK()遇到并列会跳过序号,DENSE_RANK()则不会跳。这又是另外的学问了,不过都是窗口函数里的好兄弟。

这两招,一个TOP,一个ROW_NUMBER(),基本能覆盖我在SQL Server里取前N行数据的大部分需求了。从最开始找不到“ROWNUM”的懵圈,到后来搞明白这些,我觉得我的SQL Server水平蹭蹭就上去了。说到底,知识这东西,还是得自己多动手,多踩坑,然后才能真正学到手。希望我的这点破经验,也能帮到正在SQL Server里摸爬滚打的兄弟们,少走点弯路。