SQL 改写系列八:连接消除

2024年 5月 7日 48.6k 0

SQL 改写系列八:连接消除-1

系列文章导读

100% 自主研发,连续9年稳定支撑双11,创新推出“三地五中心”城市级容灾新标准的OceanBase,是全球唯一一款在 TPC-C 和 TPC-H 测试中刷新世界纪录的国产原生分布式数据库,于 2021 年 6 月正式开放源代码。

查询优化器是关系型数据库系统的核心模块,是数据库内核开发的重点和难点,也是衡量整个数据库系统成熟度的“试金石”。为了帮助大家更好地理解 OceanBase 查询优化器,我们将撰写查询改写系列文章,带大家更好地掌握查询改写的精髓,熟悉复杂 SQL 的等价性,写出高效的 SQL。本文是 OceanBase 改写系列第八篇,将重点和大家介绍一下连接消除的技术,欢迎探讨~进入【SQL 改写专题】 查看系列内容

1.引言

在实际应用场景中,你可能经常根据业务需要,写出从多张表中进行查询的语句。那我们接着本系列第八篇的内容,仍然以电影院的为例来讲解这个问题,假设需求方想要“查询电影院中所有有排片的影片的排片信息”,这时你可能会很自然地写出如下语句:

Q1:
SELECT PLAY.* FROM PLAY LEFT JOIN MOVIE ON PLAY.ID = MOVIE.ID; 

Q1需要查询排片信息PLAY和影片信息MOVIE两张表的数据,然后通过左连接返回最终结果。从业务角度来看,上述需求的表述不够精简,我们可以将其提炼为“查询电影院中所有电影的排片信息”,Q1实际等价于:

Q2:
SELECT PLAY.* FROM PLAY; 

从SQL语义的角度来看,由于左外连接必然会返回左侧表的所有行,而主键PLAY.ID、MOVIE.ID均具有非NULL、唯一的属性,连接后的左侧表的结果仍然唯一,因此右侧表MOVIE是冗余的,据此我们同样可以得到Q2的结果。

这种在满足一定条件时,消除连接中的一张或多张表的改写规则,被称为“连接消除”。从连接方式看,除了上述示例中的外连接消除,内连接、半连接(SEMI JOIN)和反连接(ANTI JOIN)也同样可运用连接消除规则;从消除满足的条件来看,则可分为自连接连接、主键/主外键连接消除和恒FALSE连接消除等。

2.连接消除

外连接消除

在引言中,我们给出了一项简单的左外连接消除示例,该示例满足条件:

  1. 返回列表仅包含左侧表;
  2. 连接方式为外连接;
  3. 连接条件在主键ID上(非NULL且唯一)且为等值连接

显然,条件1和条件2是必要的,而条件3则非必要,例如,我们可以通过使用DISTINCT来保证查询结果的唯一性,从而使得消除右表后与消除前的结果集一致,此时则无需条件3的限制:

TABLE T1(C1)
TABLE T2(C1)
--Q3和Q4等价
Q3:
SELECT DISTINCT T1.* FROM T1 LEFT JOIN T2 ON T1.C1 = T2.C1; 
Q4:
SELECT DISTINCT T1.* FROM T1; 

在Q3中,C1列存在NULL值和重复值,和T2通过等值条件T1.C1=T2.C1进行外连接后,返回的结果集大于T1的原表行数,但是在通过DISTINCT去重后,其结果和直接对T1表的返回结果集加DISTINCT是一样的。

SQL 改写系列八:连接消除-2

SQL 改写系列八:连接消除-3

细心的读者可能已经观察到,加DISTINCT的重点在于保证连接结果的唯一性,如果T1.C1和T2.C1均为UNIQUE KEY,那么即使不加DISTINCT,也仍然可以做连接消除。

前面分别讨论了以主键、唯一键、以及普通列加DISTINCT做连接消除的场景,其它类型的键是否也可进行连接消除?答案是肯定的,例如,业务上可能会在含主外键关系的两张表上写出类似Q5的语句,根据主外键的性质,TF.C1要么为NULL,要么唯一,和T3做外连接后,不会产生额外的行数,因此可以把T3表消除,从而转换为Q6,这种消除方式被称为主外键连接消除。

TABLE T3(C1 PRIMARY KEY, C2)
TABLE TF(C1, C2, FOREIGN KEY (C1) REFERENCES T3(C1))
Q5:
SELECT TF.* FROM TF LEFT JOIN T3 ON TF.C1 = T3.C1; 
Q6:
SELECT TF.* FROM   TF; 

这里我们先来思考一个问题:上述示例均为等值连接,非等值连接的情况下,是否可以消除?

通常,在非等值连接的情况下,无论连接条件是否为主键,左表中的每一行都有可能匹配右表中的多行,从而在左表上产生重复行,此时不能做连接消除。但是若连接条件为恒FALSE,则左表中的每一行都无法和右表匹配,此时,右侧的每一行都会因为不匹配而补NULL,在这一场景下,我们可以将右侧表消除,考虑查询Q7:

Q7:
SELECT * FROM T1 LEFT JOIN T2 ON 1 = 0; 
Q8:
SELECT T1.C1, NULL FROM T1; 

根据左外连接的定义,右表中与左表不匹配的行需补NULL,由于Q7的连接条件恒FALSE,左表T1的每一行都无法与T2中的行匹配,使得右侧表全部补为NULL,换言之,我们无需关注右侧表的返回值,只需保留右侧表返回列的列名、类型等SCHEMA信息即可,因此右侧表T2可被消除。这种在恒FALSE连接条件下消除右表的方式,被称为恒FALSE连接消除。

SQL 改写系列八:连接消除-4

内连接消除

顾名思义,内连接消除是指消除内连接中的一张或多张表的改写规则,该消除通常发生在自连接中,自连接消除是指连接的两侧为同一张表的场景,考虑查询Q9:

TABLE T4(C1 UNIQUE KEY, C2)
Q9:
SELECT * FROM T4 tt1,T4 tt2 WHERE tt1.C1 = tt2.C1; 
Q10:
SELECT T4.*,T4.* FROM T4 WHERE T4.C1 IS NOT NULL; 

Q9在同一张表上进行内连接,要求满足在同一列上做等值连接,且该列具有唯一性。根据内连接的性质,在内连接时,不匹配的行会被过滤掉,由于Q9是同一张表以同一列做连接条件进行内连接,因此除NULL值外的每一行都一定匹配,所以实际上只需查询一次表,右侧的结果从左侧补上即可,从而将Q9转换为Q10。

在上一节中,我们讨论了在普通列上加DISTINCT后进行外连接消除的场景,如果把Q9中的连接条件改为TT1.C2 = TT2.C2,然后加上DISTINCT,是否可以进行内连接消除?考察查询Q11:

Q11:
--C2是普通列
SELECT DISTINCT * FROM T4 tt1,T4 tt2 WHERE tt1.C2 = tt2.C2; 

Q11的运算流程如下图所示,由于没有相同行,加DISTINCT后也无法起到去重的作用,最终结果集大于原表行数,因此无法做消除。

SQL 改写系列八:连接消除-5

当然,如果要求Q11仅返回单侧的结果,或者通过主外键进行内连接,是可以进行消除的,读者可以自行尝试构造下。

半连接/反连接消除

在文章OceanBase 改写系列二: 子查询提升首篇中,我们介绍过半连接(SEMI JOIN)和反连接(ANTI JOIN)的概念,这两种连接方式也可进行连接消除。下面分别给出SEMI /ANTI JOIN消除的示例:

Q12:
SELECT * FROM T1
WHERE  EXISTS(SELECT 1 FROM T2 LEFT JOIN T3 ON T2.C1 = T3.C1); 
Q13:
SELECT * FROM T1 WHERE EXISTS(SELECT 1 FROM T2); 

Q14:
SELECT * FROM T1
WHERE C1 NOT IN (SELECT T2.C1 FROM T2 LEFT JOIN T3 ON T2.C1 = T3.C1); 
Q15:
SELECT * FROM T1 WHERE C1 NOT IN (SELECT T2.C1 FROM T2); 

考虑查询Q12,Q12含一个EXISTS子查询,根据EXISTS子查询的语义,我们只需保证其子查询返回结果不为空即可。注意到Q12的EXISTS子查询中含有一个左外连接,而左外连接必然会返回左表的结果,即EXISTS子查询是否为真,仅取决于T2表的结果集是否为空,因此,其右侧表可全部消除,从而将Q12转换为Q13。ANTI JOIN同理。

Q12和Q14都是利用EXISTS和NOT IN的语义来消除掉子查询中的冗余连接的,我们能否把SEMI JOIN/ANTI JOIN的右侧(即EXISTS/NOT IN语句)消除掉呢?考虑查询Q16:

Q16:
SELECT * FROM T1 WHERE EXISTS(SELECT 1 FROM T1 tt WHERE T1.C1 = tt.C1); 
Q17:
SELECT * FROM T1 WHERE T1.C1 IS NOT NULL; 

Q16中的主查询遍历T1表,然后找到子查询TT表中满足T1.C1 = TT.C1的值,注意到主查询和子查询引用的均为T1表且连接条件为同一列,根据EXISTS子查询的语义,若T1.C1不全为NULL,则经过EXISTS子查询过滤后一定可返回T1.C1不为NULL的行;若T1.C1全为NULL,那么EXISTS子查询一定返回FALSE,此时结果集为空。综上,可将Q16转换为Q17。由于连接条件过滤掉了T1.C1为NULL的值,因此在消除后补上了T1.C1 IS NOT NULL这一条件;若T1.C1具有NOT NULL的限制,则该条件也可去除。

Q16的主查询和子查询中扫描的是同一张表T1,换言之,Q16也可视作自连接消除,这种做法和前一节介绍的内连接消除非常相似。实际上,Q16可通过某些规则转换为内连接,然后采用前一节的内连接消除来消除掉右侧表,我们将在后续文章中介绍这项改写规则,本文不再展开。

3.总结

本文根据连接方式,简要介绍了外连接消除、内连接消除和半连接/反连接消除三类消除场景,并根据连接的条件,引入了主键/主外键消除、自连接消除和恒FALSE连接消除。尽管本文仅介绍了查询语句的消除,但是上述规则也可应用在 DELETE、UPDATE和MERGE INTO 语句中,概括来看,连接消除要着重关注两个要点,一是连接的方式和连接条件,比如是否外连接,是否等值连接等等,例如,恒FALSE连接消除就利用了左外连接时右侧不匹配的行需补NULL的性质;二是表的定义和约束,比如是否含主键、主外键、唯一键等约束。在实际应用中,有些SQL语句并不直接满足本文所介绍的消除规则,但是通过其它改写(如子查询上拉、谓词推导等等),可得到隐含的消除条件,从而扩展连接消除的应用场景。从业务上看,有些被连接的表数据量非常大,导致连接后计算开销也相应增大,如果可通过该改写消除掉这些表,则可大大降低连接的计算开销,从而大幅提升性能。

专栏作者介绍

OceanBase 优化器团队,由 OceanBase 高级技术专家溪峰、技术专家山文等领衔,致力于打造全球领先的分布式查询优化器。

系列内容构成

本次查询改写系列不仅包括子查询优化、聚合函数优化、 窗口函数优化、 复杂表达式优化四大模块,另外还有更多模块内容,敬请期待!本文根据连接方式,向大家介绍外连接消除、内连接消除和半连接/反连接消除三类消除场景,并根据连接的条件,引入了主键/主外键消除、自连接消除和恒 FALSE 连接消除。欢迎关注 OceanBase 开源用户群钉钉号:33254054),进群与 OceanBase 查询优化器团队一同交流。

SQL 改写系列八:连接消除-6

附录:

1、OceanBase 改写系列一:OceanBase 查询改写实践概述

2、OceanBase 改写系列二: 子查询提升首篇

3、OceanBase 改写系列三:如何提升子查询性能(包含聚合函数)的最佳实践

4、OceanBase 改写系列四: 聚合分组等价变换大法之分组下压

5、OceanBase 改写系列五:视图合并设计与实践

6、OceanBase 改写系列六:谓词推导

7、SQL 改写系列七:谓词移动

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论