背景
很多文章中都声称,为了优化效率,可以使用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的性能优化也不是绝对的,这个就是另一个话题了。