MySQL 排序查询第N项 / 前N项

一 引言

工作中常会遇到 “查询工资最高/第二高/最低/倒数第N的员工” “销售量前10的商品” 此类排序取前N的问题。这篇文章以查询薪资第二高/倒数第二高的员工为例,给出了三种解决方法。

二 创建表 

CREATE TABLE `salaries` (

`emp_no` int(11) NOT NULL,

`salary` int(11) NOT NULL,

`from_date` date NOT NULL,

`to_date` date NOT NULL,

PRIMARY KEY (`emp_no`,`from_date`));

INSERT INTO salaries VALUES(10001,60117,'1986-06-26','1987-06-26'); INSERT INTO salaries VALUES(10001,62102,'1987-06-26','1988-06-25'); INSERT INTO salaries VALUES(10001,66074,'1988-06-25','1989-06-25'); INSERT INTO salaries VALUES(10001,66596,'1989-06-25','1990-06-25'); INSERT INTO salaries VALUES(10001,66961,'1990-06-25','1991-06-25'); INSERT INTO salaries VALUES(10001,71046,'1991-06-25','1992-06-24'); INSERT INTO salaries VALUES(10001,74333,'1992-06-24','1993-06-24'); INSERT INTO salaries VALUES(10001,75286,'1993-06-24','1994-06-24'); INSERT INTO salaries VALUES(10001,75994,'1994-06-24','1995-06-24'); INSERT INTO salaries VALUES(10001,76884,'1995-06-24','1996-06-23'); INSERT INTO salaries VALUES(10001,80013,'1996-06-23','1997-06-23'); INSERT INTO salaries VALUES(10001,81025,'1997-06-23','1998-06-23'); INSERT INTO salaries VALUES(10001,81097,'1998-06-23','1999-06-23'); INSERT INTO salaries VALUES(10001,84917,'1999-06-23','2000-06-22'); INSERT INTO salaries VALUES(10001,85112,'2000-06-22','2001-06-22'); INSERT INTO salaries VALUES(10001,85097,'2001-06-22','2002-06-22'); INSERT INTO salaries VALUES(10001,88958,'2002-06-22','9999-01-01'); INSERT INTO salaries VALUES(10002,72527,'1996-08-03','1997-08-03'); INSERT INTO salaries VALUES(10002,72527,'1997-08-03','1998-08-03'); INSERT INTO salaries VALUES(10002,72527,'1998-08-03','1999-08-03'); INSERT INTO salaries VALUES(10002,72527,'1999-08-03','2000-08-02'); INSERT INTO salaries VALUES(10002,72527,'2000-08-02','2001-08-02'); INSERT INTO salaries VALUES(10002,72527,'2001-08-02','9999-01-01'); INSERT INTO salaries VALUES(10003,40006,'1995-12-03','1996-12-02'); INSERT INTO salaries VALUES(10003,43616,'1996-12-02','1997-12-02'); INSERT INTO salaries VALUES(10003,43466,'1997-12-02','1998-12-02'); INSERT INTO salaries VALUES(10003,43636,'1998-12-02','1999-12-02'); INSERT INTO salaries VALUES(10003,43478,'1999-12-02','2000-12-01'); INSERT INTO salaries VALUES(10003,43699,'2000-12-01','2001-12-01'); INSERT INTO salaries VALUES(10003,43311,'2001-12-01','9999-01-01'); INSERT INTO salaries VALUES(10004,40054,'1986-12-01','1987-12-01'); INSERT INTO salaries VALUES(10004,42283,'1987-12-01','1988-11-30'); INSERT INTO salaries VALUES(10004,42542,'1988-11-30','1989-11-30'); INSERT INTO salaries VALUES(10004,46065,'1989-11-30','1990-11-30'); INSERT INTO salaries VALUES(10004,48271,'1990-11-30','1991-11-30'); INSERT INTO salaries VALUES(10004,50594,'1991-11-30','1992-11-29'); INSERT INTO salaries VALUES(10004,52119,'1992-11-29','1993-11-29'); INSERT INTO salaries VALUES(10004,54693,'1993-11-29','1994-11-29'); INSERT INTO salaries VALUES(10004,58326,'1994-11-29','1995-11-29'); INSERT INTO salaries VALUES(10004,60770,'1995-11-29','1996-11-28'); INSERT INTO salaries VALUES(10004,62566,'1996-11-28','1997-11-28'); INSERT INTO salaries VALUES(10004,64340,'1997-11-28','1998-11-28'); INSERT INTO salaries VALUES(10004,67096,'1998-11-28','1999-11-28'); INSERT INTO salaries VALUES(10004,69722,'1999-11-28','2000-11-27'); INSERT INTO salaries VALUES(10004,70698,'2000-11-27','2001-11-27'); INSERT INTO salaries VALUES(10004,74057,'2001-11-27','9999-01-01'); INSERT INTO salaries VALUES(10005,78228,'1989-09-12','1990-09-12'); INSERT INTO salaries VALUES(10005,82621,'1990-09-12','1991-09-12'); INSERT INTO salaries VALUES(10005,83735,'1991-09-12','1992-09-11'); INSERT INTO salaries VALUES(10005,85572,'1992-09-11','1993-09-11'); INSERT INTO salaries VALUES(10005,85076,'1993-09-11','1994-09-11'); INSERT INTO salaries VALUES(10005,86050,'1994-09-11','1995-09-11'); INSERT INTO salaries VALUES(10005,88448,'1995-09-11','1996-09-10'); INSERT INTO salaries VALUES(10005,88063,'1996-09-10','1997-09-10'); INSERT INTO salaries VALUES(10005,89724,'1997-09-10','1998-09-10'); INSERT INTO salaries VALUES(10005,90392,'1998-09-10','1999-09-10'); INSERT INTO salaries VALUES(10005,90531,'1999-09-10','2000-09-09'); INSERT INTO salaries VALUES(10005,91453,'2000-09-09','2001-09-09'); INSERT INTO salaries VALUES(10005,94692,'2001-09-09','9999-01-01'); INSERT INTO salaries VALUES(10006,43311,'1990-08-05','1991-08-05'); INSERT INTO salaries VALUES(10006,43311,'1991-08-05','1992-08-04'); INSERT INTO salaries VALUES(10006,43311,'1992-08-04','1993-08-04'); INSERT INTO salaries VALUES(10006,43311,'1993-08-04','1994-08-04'); INSERT INTO salaries VALUES(10006,43311,'1994-08-04','1995-08-04'); INSERT INTO salaries VALUES(10006,43311,'1995-08-04','1996-08-03'); INSERT INTO salaries VALUES(10006,43311,'1996-08-03','1997-08-03'); INSERT INTO salaries VALUES(10006,43311,'1997-08-03','1998-08-03'); INSERT INTO salaries VALUES(10006,43311,'1998-08-03','1999-08-03'); INSERT INTO salaries VALUES(10006,43311,'1999-08-03','2000-08-02'); INSERT INTO salaries VALUES(10006,43311,'2000-08-02','2001-08-02'); INSERT INTO salaries VALUES(10006,43311,'2001-08-02','9999-01-01'); INSERT INTO salaries VALUES(10007,56724,'1989-02-10','1990-02-10'); INSERT INTO salaries VALUES(10007,60740,'1990-02-10','1991-02-10'); INSERT INTO salaries VALUES(10007,62745,'1991-02-10','1992-02-10'); INSERT INTO salaries VALUES(10007,63475,'1992-02-10','1993-02-09'); INSERT INTO salaries VALUES(10007,63208,'1993-02-09','1994-02-09'); INSERT INTO salaries VALUES(10007,64563,'1994-02-09','1995-02-09'); INSERT INTO salaries VALUES(10007,68833,'1995-02-09','1996-02-09'); INSERT INTO salaries VALUES(10007,70220,'1996-02-09','1997-02-08'); INSERT INTO salaries VALUES(10007,73362,'1997-02-08','1998-02-08'); INSERT INTO salaries VALUES(10007,75582,'1998-02-08','1999-02-08'); INSERT INTO salaries VALUES(10007,79513,'1999-02-08','2000-02-08'); INSERT INTO salaries VALUES(10007,80083,'2000-02-08','2001-02-07'); INSERT INTO salaries VALUES(10007,84456,'2001-02-07','2002-02-07'); INSERT INTO salaries VALUES(10007,88070,'2002-02-07','9999-01-01'); INSERT INTO salaries VALUES(10008,46671,'1998-03-11','1999-03-11'); INSERT INTO salaries VALUES(10008,48584,'1999-03-11','2000-03-10'); INSERT INTO salaries VALUES(10008,52668,'2000-03-10','2000-07-31'); INSERT INTO salaries VALUES(10009,60929,'1985-02-18','1986-02-18'); INSERT INTO salaries VALUES(10009,64604,'1986-02-18','1987-02-18'); INSERT INTO salaries VALUES(10009,64780,'1987-02-18','1988-02-18'); INSERT INTO salaries VALUES(10009,66302,'1988-02-18','1989-02-17'); INSERT INTO salaries VALUES(10009,69042,'1989-02-17','1990-02-17'); INSERT INTO salaries VALUES(10009,70889,'1990-02-17','1991-02-17'); INSERT INTO salaries VALUES(10009,71434,'1991-02-17','1992-02-17'); INSERT INTO salaries VALUES(10009,74612,'1992-02-17','1993-02-16'); INSERT INTO salaries VALUES(10009,76518,'1993-02-16','1994-02-16'); INSERT INTO salaries VALUES(10009,78335,'1994-02-16','1995-02-16'); INSERT INTO salaries VALUES(10009,80944,'1995-02-16','1996-02-16'); INSERT INTO salaries VALUES(10009,82507,'1996-02-16','1997-02-15'); INSERT INTO salaries VALUES(10009,85875,'1997-02-15','1998-02-15'); INSERT INTO salaries VALUES(10009,89324,'1998-02-15','1999-02-15'); INSERT INTO salaries VALUES(10009,90668,'1999-02-15','2000-02-15'); INSERT INTO salaries VALUES(10009,93507,'2000-02-15','2001-02-14'); INSERT INTO salaries VALUES(10009,94443,'2001-02-14','2002-02-14');

三 查询第三高薪资,如果没有第三高,用null表示


四 查询to_date="9999-01-01",薪资最高的员工

1. max函数只能取出一条

2. 改进一下,把结果作为where子查询

思路:查询出最高工资,作为子查询,where salary=最高工资的员工

看来只有一个员工薪水等于94692,没有重复项

五  查询to_date="9999-01-01",薪资第二高的员工

先把所有的salary查出来看一下,第二高的是88958。

1. 法一: 第二名的特殊查法

这种方法只能用于取第二名。取其他的TopN不能用,如果有多个并列第二工资的员工只能查出一个

2. 法一修改,可以查出所有工资并列第二的员工

法一的基础上嵌套一下,salary作为查询条件,法一的代码作为子查询。

思路:查询出第二工资作为子查询,where salary=第二工资的员工

3. 法二:order by + limit

这种方法实际上取出的是排列后第二行的记录,只能取出一个记录,无法查出所有并列第二的记录

如果有两个薪资并列第一,查出来的可能是薪资第一的记录。

这题第一名和第二名只有一个,可以用这种方法,但实际上并不严谨。

4. 法二修改,可以查出所有工资并列第二的员工

思路:查询出第二工资作为子查询(salary加distinct),where salary=第二工资的员工

5 法三:排名法(通用方法)

原理:假如 a 排第10,意味着前面有9个数比a大,有10个数大于等于a, 求某个数的排名就是求前面有几个数大于等于这个数

思路:重复用salaries表,命名为s1,s2。用where语句s1.salary<=s2.salary筛选出所有大于等于s1.salary的s2.salary。然后统计每个s1.salary有多少个s2.salary大于等于它,就能得到每个s1.salary的排名

5.1 步骤一

重复用salaries表,命名为s1,s2。用where语句s1.salary<=s2.salary筛选出所有大于等于s1.salary的s2.salary。

例如:当salary=88958时,大于等于它的有88958,84692 共2个,所以salary=88958排第2

5.2 步骤二

 count( distinct s2.salary) :统计每个s1.salary有多少个s2.salary大于等于它得到每个s1.salary的排名

5.3 步骤三

那怎么取得第二名的数呢? 只需要限制条件,rank=2

不用group by (使用group by会导致s1.salary被去重)


六 查询to_date="9999-01-01",薪资倒数第二的员工 

先把所有的salary查出来看一下,倒数第二高是72527。

1. 法一:order by + limit 

这种方法用在这里为什么出错了呢? 因为 limit 1,1 取出来的是第二行的值。这题中,最高薪水有两个,取出来的第二行是第二个最高薪水。

2. 法一改进:给 salary 加 distinct 去重

去重后,第一名只有一行记录,limit 1,1取到的是第二的薪水。但这种方法只能查询salary,不能查出对应的员工信息

3. 法一再改进:salary作为查询条件,嵌套子查询

4. 法三: 排名法

上面说过,求某个数的排名就是求前面有几个数大于等于这个数,假如 a 排第10,意味着前面有9个数比a大,有10个数大于等于a。

那倒数也是一样的,假如a排倒数第二,意味着比它小的数只有一个,有两个数小于等于它(比它小的数和它本身)

求某个数的倒数排名,就是求小于等于它的数有几个

从结果可以看出,salary=72527时,只有72525本身和43311比它小。说明salary=72525可能是倒数第二的薪资。接下来,只需要统计 每个s1.salary 对应的比它小的 s2.salay 有几个,就能得到salary的倒数名次。

那么怎么用代码查询倒数第二呢,和前面查正数排名一样,只需要限制排名=2

不用group by (使用group by会导致s1.salary被去重)


六 查询to_date="9999-01-01",薪资前三名

法一:order by+limit 

实际上取出的是排在前三行的工资,这题的数据集工资没有重复,可以这样做,但如果工资有重复,这样做就不严谨

法二:排名法

七 给员工薪资排名


八 其他


思路:可以看成两个表,一个是id,name,salary.  managerid 另一个是managerid,name,salary。

 通过 managerid关联两个表。变成 id,name,salary,managerid,managerid(1),salary.这样就把员工salary和经理salary匹配起来了。

但现在是同一个表,所以做法是同一个表用两次。本应在manager表里的managerid变成这里的id


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容