Mysql中exists子查询语句的使用,取出每组中最高的前n名的信息

一、准备测试的表和数据

 

[sql] view plain copy
 
  1. create table cat(  
  2.     id int not null auto_increment primary key,  
  3.     cat_id int,  
  4.     value int,  
  5.     name varchar(30)  
  6. );  


二、插入一些测试数据

 

 

[sql] view plain copy
 
  1. insert into cat (cat_id,name,value) values ('1','name1', '2');  
  2. insert into cat (cat_id,name,value) values ('1','name2', '21');  
  3. insert into cat (cat_id,name,value) values ('1','name3', '1');  
  4. insert into cat (cat_id,name,value) values ('1','name4', '3');  
  5. insert into cat (cat_id,name,value) values ('2','name5', '54');  
  6. insert into cat (cat_id,name,value) values ('2','name6', '4');  
  7. insert into cat (cat_id,name,value) values ('2','name7', '24');   
  8. insert into cat (cat_id,name,value) values ('2','name8', '23');  
  9. insert into cat (cat_id,name,value) values ('3','name9', '57');  
  10. insert into cat (cat_id,name,value) values ('3','name10','45');  
  11. insert into cat (cat_id,name,value) values ('3','name11','12');  
  12. insert into cat (cat_id,name,value) values ('3','name12','23');  


基本工作准备完毕,回到正题,在实际开发环境中,有时需要查询每个分组例如销量最高的前几名等等。可以使用如下SQL语句(此处是Mysql):

 

 

取出每组中value值最高的前三条记录:

select a.* from cat a where exists (select count(*) from cat where cat_id = a.cat_id and value > a.value having Count(*) < 3) order by a.cat_id,a.value desc;

 


exists的子查询中,有引用外层查询,现在来分析:

 

首先,按照sql执行顺序,先看from部分,毫无疑问,查询了所有的表记录,等同于select * from cat,记录如下:

 

[sql] view plain copy
 
  1. mysql> select * from cat;  
  2. +----+--------+--------+-------+  
  3. | id | cat_id | name   | value |  
  4. +----+--------+--------+-------+  
  5. |  1 |      1 | name1  |     2 |  
  6. |  2 |      1 | name2  |    21 |  
  7. |  3 |      1 | name3  |     1 |  
  8. |  4 |      1 | name4  |     3 |  
  9. |  5 |      2 | name5  |    54 |  
  10. |  6 |      2 | name6  |     4 |  
  11. |  7 |      2 | name7  |    24 |  
  12. |  8 |      2 | name8  |    23 |  
  13. |  9 |      3 | name9  |    57 |  
  14. | 10 |      3 | name10 |    45 |  
  15. | 11 |      3 | name11 |    12 |  
  16. | 12 |      3 | name12 |    23 |  
  17. +----+--------+--------+-------+  
  18. 12 rows in set (0.00 sec)  

然后,exits子查询的特点是返回true或者是false,并不会返回结果集,这点很重要!等同于外层查询通过where了一个exists的子查询返回的是true还是false来判断外层查询的这条记录是否保存到结果集中。

 

进一步了解执行的过程:

现在,我拿到外层查询的第一行,也就是上图的第一行。此时子查询中取出当前表cat中cat_id=1并且values值大于2的,毫无疑问,有2条记录,满足条件小于3(用到了having语句,对中间结果进行过滤等等处理),此时exists满足条件,返回true。外层查询第一条记录被保存。到最后cat_id为1,value为1的,count(*)为3,子查询返回的是false。所以上图中的第3条记录不会被保存。

 

然后一番选取之后,外层查询得到了满足条件的结果集。最后通过order by将结果集排序,然后显示如下:

 

[sql] view plain copy
 
    1. +----+--------+--------+-------+  
    2. | id | cat_id | name   | value |  
    3. +----+--------+--------+-------+  
    4. |  2 |      1 | name2  |    21 |  
    5. |  4 |      1 | name4  |     3 |  
    6. |  1 |      1 | name1  |     2 |  
    7. |  5 |      2 | name5  |    54 |  
    8. |  7 |      2 | name7  |    24 |  
    9. |  8 |      2 | name8  |    23 |  
    10. |  9 |      3 | name9  |    57 |  
    11. | 10 |      3 | name10 |    45 |  
    12. | 12 |      3 | name12 |    23 |  
    13. +----+--------+--------+-------+  
    14. rows in set (0.00 sec)  
posted @ 2018-01-29 14:01  纯洁的赤子之心  阅读(283)  评论(0)    收藏  举报