天天看點

SQL查詢性能優化 之 LEFT JOIN 替換 NOT IN

       今天工作的時候遇到一個需求,如下:

       有體育賽事資訊實體Game,Game可以在背景管理系統中被指定為APP或者PC網站的首頁推薦,當一個賽事(Game)被指定為首頁推薦後,在用戶端看到的效果是,被推薦賽事的一張大圖加入到首頁輪播(HomeSlideShow)中,點選圖檔,即可跳轉到相對應的賽事詳情頁。

       客戶要求每個賽事賽事(Game)隻能被推薦一次,不能重複推薦。

       現有賽事資訊表game, 和首頁推薦表slideshow,字段如下

Game
ID varchar(50)
NAME varchar(50)
SlideShow
ID varchar(50)
GID varchar(50)
TITLE varchar(100)

       在組織SQL語句,查詢未添加過首頁推薦的賽事(Game)時,很容易寫成:

SELECT ID, NAME FROM Game WHERE ID NOT IN (SELECT GID FROM SlideShow);
           

       我們知道NOT IN的查詢效率是非常低的,因為它不能使用索引,大大降低查詢效率。

       那麼,在需要NOT IN的時候如何用什麼替代它能?

       常見的可以使用LEFT JOIN...IS NULL的方式達到同樣的目的。

       例如上面的SQL可以改成:

SELECT ID, NAME FROM (SELECT g.ID, g.NAME s.GID FROM Game g LEFT JOIN SlideShow s ON(g.ID = s.GID)) c WHERE c.GID IS NULL;           

       這裡主要用到了左向外聯接的結果集包括  LEFT (OUTER)子句中指定的左表(這裡指Game)的所有行,而不僅僅是聯接列(Game.ID和SlideShow.GID)所比對的行。如果左表(Game)的某行在右表(SlideShow)中沒有比對行,則在相關聯的結果集行中右表的所有選擇清單列均為空值。

       也就是說,我們把Game表和SlideShow兩張表拿出來按照Game.ID = SlideShow.GID的規則對比一下,生成一個臨時表c(也可以了解成把兩張表按照上述規則合并一下,合并後的表是c,這裡并沒有真正生成實體上的表c)。理論上SlideShow中GID都可以在Game表的ID列找到對應的值,反之在SlideShow的GID的列并不一定能找到Game表中ID列上的所有值,是以,我們通過左聯接查詢,發現c.GID的為空,那麼就意味着該行的c.ID就是我們需要的Game的ID。

       雖然這篇文章的标題是用LEFT JOIN 代替 NOT IN,但在上面的解釋中,我并沒有說一定要用左外連接配接代替NOT IN,而不能用RIGHT JOIN。因為RIGHT JOIN也是可以的,道理都是一樣的。例如你也可以把上面的左外聯接改成下面的右外連接配接查詢:

SELECT ID, NAME FROM (SELECT g.ID, g.NAME s.GID FROM SlideShow s RIGHT JOIN Game g ON(g.ID = s.GID)) c WHERE c.GID IS NULL;           

       具體NOT IN如何影響SQL性能、以及上述方法為什麼比用NOT IN效率更高,除了和使用索引有關外,更深入的我也在學習中。也希望能得到各位前輩的指點!