背景
很多文章中都聲稱,爲了優化效率,可以使用exists去替代in,但實際上在一些特殊業務情況下,兩者並不能等同。
實例
今天剛好記錄一下碰到的情況,以ORACLE爲例,EXISTS用於校驗子查詢是否爲true,而in用於判斷條件是否位於子查詢中,現需求如下,獲取某日期段內的用戶所有以往記錄,比如
A、2014-06-01~2014-06-02有操作記錄的用戶
B、根據該用戶ID找到所有該用戶操作記錄
用IN方式則可以很簡單得出結果
SELECT
TMP.patient_id,
TMP.event_no,
TMP.retrieve_date
FROM
t_user TMP
WHERE
TMP.PATIENT_ID IN (
SELECT
AP.PATIENT_ID
FROM
t_user ap
WHERE
AP.retrieve_date >= TO_DATE ('2014-06-01', 'yyyy-MM-dd')
AND AP.retrieve_date <= TO_DATE ('2014-06-02', 'yyyy-MM-dd')
)
order by TMP.patient_id desc
而使用EXISTS的話,則達不到想要的結果,條件被過濾了,只能獲得2014-06-01~2014-06-02的記錄
SELECT
TMP.patient_id,
TMP.event_no,
TMP.retrieve_date
FROM
t_user TMP
WHERE
EXISTS (
SELECT
AP.PATIENT_ID
FROM
t_user ap
WHERE
AP.patient_id = TMP.patient_id
AND AP.event_no = TMP.event_no
AND AP.orgcode = TMP.orgcode
AND AP.retrieve_date >= TO_DATE ('2014-06-01', 'yyyy-MM-dd')
AND AP.retrieve_date <= TO_DATE ('2014-06-02', 'yyyy-MM-dd')
)
而如果將內外表表關聯條件去掉的話,則全部用戶數據都查出來了,也顯然不符合要求。
分析
exists的意義在於子查詢是否爲真,如果不與外表關聯的話,則條件恆爲真的情況下,外表所有數據的where條件也恆爲真,所以所有數據全部可查;而與外表關聯的話,則變成了外表的數據必須滿足內表條件,比如外表數據也必須在2014-06-01~2014-06-02之間才能使內表條件爲真。
這個時候IN則避開了這點,只要內表的數據內容與外表條件匹配即可,查詢即等價於
select * from A where A.id in (‘1001’,‘1002’,‘1003’)
總結
所以EXISTS與IN的互換還需要具體問題具體分析,否則很可能得到錯誤的結果。EXISTS對IN的性能優化也不是絕對的,這個就是另一個話題了。