我的编程空间,编程开发者的网络收藏夹
学习永远不晚

数据库性能优化之IN子查询优化

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

数据库性能优化之IN子查询优化

问题定义

为了获取最近一年内有订单的用户信息,可以使用以下的三种写法去实现,它们在语义上是等价的。那它们的性能如何,适用场景是什么?这是本文讨论的主题。

  • 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子查询的伪代码实现逻辑:

  1. 执行子查询语句,并得到结果集并去重,并将结果集存储在临时表中。
  2. 将主查询中的值逐一与子查询结果集中的值进行比较,如果匹配成功,则返回该行数据。
  3. 在第二步的比较时。
  • 可以将子查询的结果集转化为一个哈希表,然后对于主查询中的每一行,都在哈希表中查找该行的值是否存在。
  • 可以在上面建立一个唯一性索引,通过此索引和外表进行关联。不论适用哪一种方式,它的实际复杂度都为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)

实现逻辑如下:

  1. 对于主查询中的每一行,都执行一次子查询。
  2. 如果子查询返回的结果集不为空,则保留该行数据。

时间复杂度

因此它的时间复杂度为O(m*n), 其中m为外表的记录数,n为子查询的访问的记录数。

  • 如果子查询中的orders没有索引,则n为orders表的行数。
  • 如果orders上有筛选率比较大的索引,则n为索引所筛选出的记录数。

可以看出,如果EXISTS的子查询中有筛选率非常高的索引,使用EXISTS子查询的性能比较好。

Join方式

为了保证语义一致性,使用join方式需要先进行去重操作。

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子查询的执行计划,可以看到Join方式就是IN子查询的执行计划的SQL化表达。

如果如果子查询中的查询列是唯一的,那么可以将其转换为内连接,从而获得更好的性能。

数据库中的IN子查询优化

事实上,MySQL和PostgreSQL都可以对IN和EXISTS采取最优的执行计划。

如果没有O_ORDERDATE上的索引,Query1和Query2在MySQL上的执行计划都是采用IN子查询的伪代码实现逻辑:

-> Nested loop inner join  (cost=19847117.66 rows=198449671)
   -> Table scan on customer  (cost=1155.80 rows=9948)
   -> Single-row index lookup on  using  (o_custkey=customer.C_CUSTKEY)
       -> Materialize with deduplication  (cost=22471.48..22471.48 rows=19949)
           -> Filter: (orders.O_ORDERDATE = ((curdate() - interval 1 year)))  (cost=20476.61 rows=19949)
               -> Table scan on orders  (cost=20476.61 rows=199487)

如果在O_ORDERDATE建立一个索引,那么它们的执行计划都是采用EXISTS子查询的伪代码实现逻辑:

-> Nested loop semijoin  (cost=22777.29 rows=5705)
   -> Table scan on customer  (cost=1155.80 rows=9948)
   -> Filter: (orders.O_ORDERDATE = ((curdate() - interval 1 year)))  (cost=0.92 rows=1)
       -> Index lookup on orders using o_idx_key (O_CUSTKEY=customer.C_CUSTKEY)  (cost=0.92 rows=6)

如果子查询中的查询列是唯一的,那么数据库会将其转换为内连接。

譬如对于下面的SQL。

select * from orders where o_custkey in (select c_custkey from customer where c_phone like '139%')

MySQL的执行计划是这样的(PostgreSQL也是类似的):

-> Nested loop inner join  (cost=3541.61 rows=6313)
   -> Filter: (customer.C_PHONE like '139%')  (cost=1148.89 rows=1099)
       -> Table scan on customer  (cost=1148.89 rows=9888)
   -> Index lookup on orders using idx_orders_ckey (O_CUSTKEY=customer.C_CUSTKEY)  (cost=1.60 rows=6)

可以看出,在MySQL和PostgreSQL数据库中,使用IN或是EXISTS的写法是等价的,数据库总是可以根据索引和统计信息采用最优的执行计划。

PawSQL中的IN子查询优化

PawSQL中会将IN子查询重写为EXISTS子查询或是内连接查询,从而帮助索引推荐引擎推荐合适的索引,促使优化器采用最优的执行计划。

IN子查询转换为EXISTS

原SQL:

select *
from tpch.customer
where customer.c_custkey in (
          select orders.o_custkey
          from tpch.orders
          where orders.O_ORDERDATE >= current_date - interval '1' YEAR)

应用重写优化,转换为:

select  *
from tpch.customer
where exists (select  orders.o_custkey
            from tpch.orders
            where orders.O_ORDERDATE >= current_date - interval '1' YEAR
        and orders.o_custkey = customer.c_custkey)

基于转换后的SQL,推荐索引:

CREATE INDEX PAW_IDX1072908633 ON tpch.ORDERS(O_ORDERDATE,O_CUSTKEY);
-- 当QB_2中引用的表ORDERS作为驱动表时, 索引PAW_IDX1072908633可以被用来进行索引范围查找,过滤条件为(orders.O_ORDERDATE >= current_date - interval '1' YEAR); 该索引是个覆盖索引,可以避免回表.

性能验证:

  • 执行计划(优化前)
-> Nested loop inner join  (cost=65987720.69 rows=659855821)
 -> Table scan on customer  (cost=1149.80 rows=9888)
 -> Single-row index lookup on  using  (o_custkey=customer.C_CUSTKEY)
     -> Materialize with deduplication  (cost=13874.51..13874.51 rows=66733)
         -> Filter: (orders.O_ORDERDATE >= ((curdate() - interval '1' year)))  (cost=7201.21 rows=66733)
             -> Table scan on orders  (cost=7201.21 rows=200219)
  • 执行计划(优化后)
-> Nested loop inner join  (cost=3771444.20 rows=37693056)
 -> Table scan on customer  (cost=1149.80 rows=9888)
 -> Single-row index lookup on  using  (o_custkey=customer.C_CUSTKEY)
     -> Materialize with deduplication  (cost=1150.65..1150.65 rows=3812)
         -> Filter: (orders.O_ORDERDATE >= ((curdate() - interval '1' year)))  (cost=769.45 rows=3812)
             -> Covering index range scan on orders using PAW_IDX1072908633 over ('2022-03-28' <= O_ORDERDATE)  (cost=769.45 rows=3812)

本次优化实施后,预计本SQL的性能将提升 1648.67%。

IN子查询转换为内连接

原SQL,c_custkey是customer表的主键。

select *
  from tpch.orders
  where orders.o_custkey in (
             select customer.c_custkey
             from tpch.customer)

应用重写优化,转化为内连接。

select orders.*
  from tpch.orders, tpch.customer
  where customer.c_custkey = orders.o_custkey

基于转换后的SQL,推荐索引。

CREATE INDEX PAW_IDX0455857015 ON tpch.ORDERS(O_CUSTKEY,O_CLERK);
  -- 当ORDERS作为被驱动表时, 索引PAW_IDX0455857015可以被用来进行索引查找, 过滤条件为(customer.c_custkey = orders.o_custkey).

性能验证。

  • 执行计划(优化前)
-> Nested loop inner join  (cost=240790.71 rows=200219)
 -> Table scan on orders  (cost=20549.81 rows=200219)
 -> Single-row covering index lookup on customer using key_idx (C_CUSTKEY=orders.O_CUSTKEY)  (cost=1.00 rows=1)
  • 执行计划(优化后)
-> Nested loop inner join  (cost=21289.23 rows=53135)
 -> Index scan on customer using key_idx  (cost=1149.80 rows=9888)
 -> Index lookup on orders using PAW_IDX0455857015 (O_CUSTKEY=customer.C_CUSTKEY)  (cost=1.50 rows=5)

本次优化实施后,预计本SQL的性能将提升 1064.60%

关于PawSQL

PawSQL专注数据库性能优化的自动化和智能化,支持MySQL,PostgreSQL,Opengauss等,提供的SQL优化产品包括

  • PawSQL Cloud,在线自动化SQL优化工具,支持SQL审查,智能查询重写、基于代价的索引推荐,适用于数据库管理员及数据应用开发人员。
  • PawSQL Advisor,IntelliJ 插件, 适用于数据应用开发人员,可以IDEA/DataGrip应用市场通过名称搜索“PawSQL Advisor”安装。
  • PawSQL Engine, 是PawSQL系列产品的后端优化引擎,可以独立安装部署,并通过http/json的接口提供SQL优化服务。PawSQL Engine以docker镜像的方式提供部署安装。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

数据库性能优化之IN子查询优化

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

数据库性能优化之IN子查询优化

PawSQL Cloud,在线自动化SQL优化工具,支持SQL审查,智能查询重写、基于代价的索引推荐,适用于数据库管理员及数据应用开发人员。
PawSQL数据库2024-11-30

Mysql查询优化之IN子查询优化方法详解

这篇文章主要给大家介绍了关于Mysql查询优化之IN子查询优化的相关资料,需要的朋友可以参考下
2023-02-09

PHP接口性能优化之数据库查询优化策略(如何优化PHP接口中的数据库查询以提升性能?)

优化PHP接口中的数据库查询对性能至关重要。最佳实践包括使用预处理语句、绑定参数、索引数据、优化查询结构、缓存查询结果、利用索引覆盖、减少查询数据量、使用快速数据库连接、监控查询并升级数据库软件。遵循这些策略有助于显着提升PHP接口的响应时间和吞吐量。
PHP接口性能优化之数据库查询优化策略(如何优化PHP接口中的数据库查询以提升性能?)
2024-04-02

数据库查询性能优化指南

数据库查询性能优化一直是程序员绕不开的话题,当我们遇到业务刷新报表缓慢或者查询获取结果延迟太大,可以采用提问法来思考如何进行优化。

Golang 技术性能优化中如何优化数据库查询?

优化 go 中的数据库查询可以提升技术性能。可以通过使用索引、减少查询返回字段、使用批量查询和使用连接池来实现。通过优化字段选择,可以减少查询响应的大小,提高查询速度。Go 中优化数据库查询以提升技术性能在 Go 应用中,数据库查询的性能
Golang 技术性能优化中如何优化数据库查询?
2024-05-12

MySQL数据库查询性能优化策略

优化查询使用Explain语句分析查询语句 Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explain 结果来优化查询语句。 通过对查询语句的分析,可以了解查询语句的执行情况,找出查询语句执行的瓶颈,从而优化查询
2022-05-25

MySQL之select in 子查询优化的实现

下面的演示基于MySQL5.7.27版本 一、关于MySQL子查询的优化策略介绍:子查询优化策略 对于不同类型的子查询,优化器会选择不同的策略。1. 对于 IN、=ANY 子查询,优化器有如下策略选择:semijoinMaterializa
2022-05-13

怎么优化NoSQL数据库的查询性能

要优化NoSQL数据库的查询性能,可以考虑以下几个方面:数据模型设计:设计合理的数据模型可以减少查询的复杂度,提高查询性能。可以根据实际需求对数据进行合理划分和建模。索引优化:在NoSQL数据库中,可以通过创建合适的索引来加快查询速度。确保
怎么优化NoSQL数据库的查询性能
2024-05-07

mysql-查询性能优化

1、不要取出全部列,取出全部列,会让优化器无法完成索引覆盖扫描这类优化,还会为服务器带来额外的I/O、内存和CPU的消耗。应该严格禁止SELECT * 的写法。MySQL使用如下三种方式应用WHERE条件,从好到坏依次为:  1.1 在索引中使用WHERE条件
mysql-查询性能优化
2021-02-18

如何通过php函数优化数据库查询性能?

数据库查询是Web开发中经常使用的功能,然而,不当的查询方式可能导致性能问题。在PHP中,我们可以通过一些函数来优化数据库查询性能,从而提高应用程序的响应速度。以下是一些优化数据库查询性能的具体代码示例。使用预处理语句预处理语句是一种将SQ
2023-10-21

如何优化 PHP 函数中数据库查询的性能?

回答:优化 php 函数中的数据库查询性能至关重要。原因:不必要的重叠查询无效的索引无效的查询语法优化技巧:使用缓存优化索引使用适当的查询类型限制结果集利用 explain使用 prepared statements优化 PHP 函数中数据
如何优化 PHP 函数中数据库查询的性能?
2024-04-24

PHP 性能优化:数据库优化指南

PHP 性能优化:数据库优化指南数据库的性能对 PHP 应用程序的整体性能至关重要。通过实施以下最佳实践,可以显著优化数据库性能:1. 索引优化创建必要的索引以加速查询。确保索引列包含 SELECT 查询中最常用的列。使用组合索引以
PHP 性能优化:数据库优化指南
2024-05-11

SQL优化教程之in与range查询

前言 《高性能MySQL》里面提及用in这种方式可以有效的替代一定的range查询,提升查询效率, 因为在一条索引里面,range字段后面的部分是不生效的(ps.需要考虑 ICP) 。MySQL优化器将in这种方式转化成 n*m 种组合进
2022-05-13

SQL查询优化-MySQL 性能调优

  在进行库表结构设计时,我们要考虑到以后的查询要如何的使用这些表,同样,编写 SQL 语句的时候也要考虑到如何使用到目前已经存在的索引,或是如何增加新的索引才能提高查询的性能。  想要对存在性能问题的查询进行优化,需要能够找到这些查询,下面先看下如何获取有性能问题的SQL。  如何设计最优的数据库表结构,如何建立最好
SQL查询优化-MySQL 性能调优
2024-04-18

热门标签

编程热搜

编程资源站

目录