SQL优化技巧 - IN子查询优化
· 阅读需 11 分钟
问题定义
为了获取最近一年内有订单的用户信息,可以使用以下的三种写法去实现,它们在语义上是等价的。那它们的性能如何?如何对它们进行优化?这是本文讨论的问题。
- Query1 - IN子查询(
= ANY
)
select * from customer where c_custkey in (select o_custkey from orders where O_ORDERDATE>=current_date - interval 1 year)
- Query2 - EXISTS子查询
select * from customer where exists (select * from orders where c_custkey = o_custkey and O_ORDERDATE>=current_date - interval 1 year)
- Query3 - JOIN方式
select c.* from customer c join (select distinct o_custkey from orders where O_ORDERDATE>=current_date - interval 1 year) o where o.o_custkey = c.custkey
IN子查询
IN子查询并不一定是非相关子查询,但是为了讨论方便,本文所述的IN子查询为非相关子查询。
Query1 - IN子查询(= ANY
)
select * from customer where c_custkey in (select o_custkey from orders where O_ORDERDATE>=current_date - interval 1 year)
IN子查询的伪代码实现逻辑:
- 执行子查询语句,并得到结果集并去重,并将结果集存储在临时表中。
- 将主查询中的值逐一与子查询结果集中的值进行比较,如果匹配成功,则返回该行数据。
- 在第二步的比较时,
- 可以将子查询的结果集转化为一个哈希表,然后对于主查询中的每一行,都在哈希表中查找该行的值是否存在。
- 可以在上面建 立一个唯一性索引,通过此索引和外表进行关联。不论适用哪一种方式,它的实际复杂度都为O(1)
时间复杂度
它的时间复杂度为**O(max(m,n)) + nlogn
**, 其中,m是外表的记录数,n为子查询的记录数。
可以看到,如果子查询的记录数比较大时,其时间复杂度较大,性能较差。
EXISTS子查询
Query2 - EXISTS子查询
select * from customer where exists (select * from orders where c_custkey = o_custkey and O_ORDERDATE>=current_date - interval 1 year)
实现逻辑如下:
- 对于主查询中的每一行,都执行一次子查询。
- 如果子查询返回的结果集不为空,则保留该行数据。