正确区分使用in和exists,not in和not exists

716 阅读2分钟

日常开发中,使用in和exists子查询是非常常见的,那么他们之前有什么区别,该如何选择是能达到最佳查询效果呢?

假设现在有两个表:用户表(t_user)、订单表(t_order)

用户表字段
id主键ID
username用户名
age年龄
address地址

用户表存在4条数据:

image.png

订单表字段
id主键ID
order_no订单编号
user_id用户id
amount订单金额

订单表存在两条数据:

image.png

先看两个子查询sql:

in查询

SELECT * FROM t_user WHERE id in (SELECT user_id FROM t_order)

exists查询

上面的sql语句相当于

SELECT * FROM t_user WHERE EXISTS (SELECT * FROM t_order WHERE t_order.user_id = t_user.id)

实际上区分inexists主要是考虑驱动顺序的改变(这也是性能变化的关键),如果是exists,那么是一外层为驱动表,先被驱动,如果是in,那么先执行子查询,所以in适合于外表大而内表子查询小的情况,exists适合于外表小而内表子查询大的情况。

关于not innot exists,推荐使用not exists!可以采用join的方式高效替代not exists的sql语句,比如:

查询没有下过单的用户,原sql语句:

SELECT * FROM t_user WHERE id not in (SELECT user_id FROM t_order);

采用join的方式替换:

SELECT u.*, o.user_id FROM t_user u LEFT JOIN t_order o ON u.id = o.user_id WHERE o.user_id is null

查询结果都是:

image.png

现在我往订单表批量插入数据,用户表数据条数还是4,订单表目前存在数据是419万,观察in和exists查询sql性能问题:

image.png

经过实际查询验证采用join方式替代not exists有助于性能提升,同样的需求:查询没有下过单的用户,执行:

SELECT * FROM t_user WHERE id not in (SELECT user_id FROM t_order);
5次的用时分别是:1.263s、1.279s、1.269s、1.267s、1.268s,每次not in方式查询大约是1.26s左右。
SELECT u.*, o.user_id FROM t_user u LEFT JOIN t_order o ON u.id = o.user_id WHERE o.user_id is null;
5次的用时分别是:0.741s、0.744s、0.746s、0.747s、0.801s,每次left join方式查询大约是0.75s左右。

证实了:可以采用join的方式高效替代not in/not exists的sql语句这个结论!!