需求: 将所有沒有訂單記錄的使用者取出來
1.查詢所有使用者
SELECT * FROM userinfo
2.查詢有訂單記錄的使用者(下訂單的使用者)
SELECT UserID FROM ordersinfo
3.查詢沒有訂單記錄的使用者
SELECT * FROM userinfo
WHERE UserID NOT in (SELECT UserID FROM ordersinfo )
4.sql優化,使用JOIN代替
SELECT * FROM userinfo
LEFT JOIN ordersinfo ON userinfo.UserID=ordersinfo.UserID
WHERE ordersinfo.UserID IS NULL
優化分析:
邏輯上,查的是使用者,兩個表都有使用者ID(UserID),要查詢沒有下單的,如果是等值連接配接,就是查詢的所有下單的使用者,左外連接配接的話,既包含下單的使用者,也包含沒有下單的使用者,下單的和沒下單的差別在于,訂單記錄表的使用者ID(UserID)是否為null。
前提是能做等值連接配接,才能做左外連接配接。
原理上,無索引的情況:比如你要查詢id為123的記錄,mysql根本不知道到底是否存在id等于123的記錄,隻能把資料表從頭到尾掃描一遍,此時有多少個磁盤塊就需要進行多少IO操作,是以查詢速度很慢。
子查詢會在記憶體中建立臨時表來完成這個邏輯上的需要兩個步驟的查詢工作,而你建立的索引在原來的表上,導緻我們建立的索引起不到我們想要的作用。
是以Mysql子查詢很慢,子查詢導緻索引不起作用,沒有索引就會全表掃描,要避免使用子查詢。
而連接配接(JOIN)不需要在記憶體中建立臨時表,是以更有效率一些。
補充:
和in也有關系。
把3步拆開來看。
SELECT count(*) FROM userinfo; //大約0.01sec
SELECT count(*) FROM ordersinfo ; //大約0.01sec
SELECT * FROM userinfo WHERE UserID NOT in (SELECT UserID FROM ordersinfo ) ; //大約33.58sec
為什麼會這麼慢呢?
官方文檔解釋:in 子句在查詢的時候有時會被轉換為 exists 的方式來執行,變成逐條記錄進行周遊(版本 5.5 中存在,5.6 中已做優化)。
參考:
https://dev.mysql.com/doc/refman/5.5/en/subquery-optimization.html
解決方案(5.5版)
1.使用臨時表
SELECT * FROM userinfo WHERE UserID NOT in (SELECT UserID FROM (SELECT UserID FROM ordersinfo)as tb ) ;
2.使用join
SELECT * FROM userinfo
LEFT JOIN ordersinfo ON userinfo.UserID=ordersinfo.UserID
WHERE ordersinfo.UserID IS NULL
版本 5.6 已針對子查詢做了優化,方式跟使用臨時表的方式一樣。