之前碰到了一道題,下面簡要說明一下:
表結構如下:
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
