之前碰到了一道题,下面简要说明一下:
表结构如下:
CREATE TABLE `testa` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cid` int(11) DEFAULT NULL comment ‘产品ID‘,
`uid` int(11) DEFAULT NULL comment ‘用户ID‘,
`buytime` int(11) DEFAULT NULL comment ‘购买时间‘,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf-8;
采用如下sql,随机插入一批测试数据:
INSERT into testa (cid,uid,buytime) values (rand()*5,rand()*5,rand()*10000);
说明:rand()返回0到1的随机小数,例如:0.4578285091163856,由于上表几个字段均为int类型在数据库存储时会自动进行四舍五入取整存储。
好了,下面进入正题:在该表内查出每个产品最早购买该产品的三个客户ID。描述好简单的一道题,当时难住了博主,经过多方查询资料,终于搞到了正确答案:
select * from testa as a where NOT EXISTS (select * from testa as b where a.cid = b.cid and a.buytime < b.buytime group by b.cid HAVING count(*) > 2);
这里最关键的几个个关键方法,exists 、 group by 和 having;
按照单个理解:exists为判断括号里面表达式返回结果是否为真,返回真则将当前主表的数据取出,否则放弃取出该结果(博主写该片文章时只了解这么多,还未深入)NOT EXISTS和EXISTS作用互斥,不做过多解释。
group by 不用过多解释,即将当前查询结果按照某个字段进行分组,去重。
having 的作用是将mysql执行完毕的最终结果进行二次处理的方法。
先说一下刚拿到这条sql的时候百思不得其解,认为exists应该和in的功能类似,先执行括号里面的方法,得到全部结果之后返回给主表进行二次查询,但是group by 之后的数据条件根本不能满足,最终还是求助于万能的因特奈特,终于明白该条sql得处理过程了,如下说明 :
因为group by 是要在where条件之后才会执行,所以会先执行子查询 中a表和b表的where关联查询,执行完where条件之后返回true,所以exists会执行为真。
这里说明一下exists执行过程:首先在a表拿第一条数据和b表的所有数据进行关联,得到所有大于当前a表查询数据的购买时间的b表数据,按照b表的产品进行分组,并且count(*)会记住当前结果获取到的行数,虽然having这时候还未生效;然后执行a表第二条数据,依次循环执行完a表所有数据之后,通过having进行结果整理,将所有查询行数大于2的全部抛弃(NOT EXISTS),得到最终结果。
说明:因为我们要取最早的三个客户,所以购买时间越小排名越高,并且当执行第一早客户的时候,a.buytime < b.buytime该条件count(*) 返回结果为0。
重新整理后sql可以这么写:select * from testa as b where EXISTS (select * from testa as c where c.cid = b.cid and b.buytime <= c.buytime group by c.cid HAVING count(*) <= 3);
借阅文章地址:
http://blog.csdn.net/qsyzb/article/details/12523051
http://fucheng.blog.51cto.com/2404495/1575693