欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

改写优化SQL(2):not in改为left join

程序员文章站 2022-05-09 15:58:33
...

在sql语句中,not in是经常会用到的一种写法,因为这种写法很直观,容易理解。

但如果不注意的话,很容易写出错误的sql,而且性能存在严重问题,所以,不建议使用not in,要尽量把 not in写法,改为left join。

一、建表

CREATE TABLE tb_emp
(
emp_id INT NOT NULL PRIMARY KEY CLUSTERED,
emp_name VARCHAR(20) NOT NULL,
tel VARCHAR(20) null
)

INSERT INTO dbo.tb_emp
VALUES
(1, '张三','1234567890'),
(2, '李四','9876543210'),
(3, '王五','12345'),
(4, '孙六','666666666'),
(5, '半泽直树','8888888888');


CREATE TABLE tb_org
(
org_id INT NOT NULL PRIMARY KEY CLUSTERED,
org_name VARCHAR(30) NOT NULL,
parent_org_id INT NULL,
emp_id INT NULL
)


INSERT INTO tb_org(org_id,org_name,parent_org_id,emp_id)
VALUES
(1, '行长', NULL,1),
(2, '常务', 1,2),
(3, '专务', 1,3),
(5, '营业二部次长', 4,null);

二、not in写法

现在要查询,tb_emp表中有,但是在tb_org中不存在的emp_id,代码如下:

SELECT e.emp_id,
       e.emp_name
FROM tb_emp e
WHERE e.emp_id NOT IN (SELECT emp_id FROM tb_org)
貌似,不用多想,代码就是上面这样子。

照理,查询结果应该返回2条数据,但奇怪的是一条也没有:

改写优化SQL(2):not in改为left join

执行计划:

改写优化SQL(2):not in改为left join

这个执行计划中,嵌套循环所采用的是 Left Anti Semi join,本质上是 Left join,其中 Anti表示反连接,Semi表示半开连接,合在一起就是 tb_emp表每条记录中的emp_id,如果在tb_org中有一个对应上的,那么tb_emp表的这条记录就不返回。

这里,sql server优化器把not in转成left join+Anti+Semi 了。


其实,这里涉及到not in写法的一个坑,就是当not in 子查询中有null值,那么整个查询的结果就是空的。

因为这里tb_org表中,org_id=5的这条记录的emp_id是null,所以返回结果就是空集。

正确的写法是:

SELECT e.emp_id,
       e.emp_name
FROM tb_emp e
WHERE e.emp_id NOT IN (SELECT emp_id FROM tb_org WHERE emp_id IS NOT null)

三、把not in转成 left join写法

SELECT e.emp_id,
       e.emp_name
FROM tb_emp e
LEFT JOIN tb_org o
ON o.emp_id = e.emp_id
WHERE o.emp_id IS NULL

查询结果:

改写优化SQL(2):not in改为left join

执行计划:

改写优化SQL(2):not in改为left join

这个执行计划,和上面sql server优化器把not in转成left join+Anti+Semi 的执行计划也不太相同,这里直接用的left join,通过o.emp_id is null来过滤关联的数据。

上一篇: SQL JOIN

下一篇: Spring事务详解