基于关系型代数的 SQL 等价改写
点击蓝色“有关SQL”关注我哟
加个“星标”,天天与10000人一起快乐成长
看过我那篇《SQL 数据库小白,从入门到精通》的朋友,一定不会陌生,SQL 的数学原理,就是集合运算。
集合运算, 排名第一的交换律,是这样的:
交换律(Commutative Laws):
A ∪ B = B∪A,
A ∩ B = B ∩ A
数学就这么妙!她把复杂的逻辑,抽象成简单的符号,收敛住精美。
当然,用纯数学理论来解释SQL,我想我会被骂成狗头。我的目的,是还原精简的符号,用实例来演绎背后的逻辑。
这里的A,B,是集合表达式。可以看成 SQL 的 where 驱动出的数据集。
比如有同学表如下:
CREATE TABLE dbo.STUDENTS(STUDENT_ID INT
, STUDENT_NAME NVARCHAR(256)
, STUDENT_GENDER NVARCHAR(6))
字段分别代表:
STUDENT_ID: 学号
STUDENT_NAME:姓名
STUDENT_GENDER:性别
假使 A 逻辑是 :
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男'
B 逻辑是:
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'女'
那么
A ∪ B = B∪A,
则可以表达为 :
--A ∪ B SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男' OR STUDENT_GENDER=N'女'--B∪A
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER=N'女' OR STUDENT_GENDER = N'男'
或者表达为:
--A ∪ B SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男'UNION SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER=N'女'--B∪ASELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER=N'女'UNION SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男'
再或者:
--A ∪ B SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男'UNION ALL SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER=N'女'--B∪ASELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER=N'女'UNION ALL SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男'
这 3 对(Or,Union, All Union ) 2 组,都是用来抓取全部的同学,那么有什么不一样吗?为什么可以有六种写法
聪明如你一定能想到,其实我这么写出来,肯定是有不一样的地方。
本质上,这 6 条语句,完成同一件事,但写法的复杂度,肉眼可见的递增。性能,也是逐个渐好。这一点,与大多数初学者的直觉相反。
没错,这才是本文要讲的重点,基于关系型代数的SQL等价改写
我记得,有一次做报表,肯兹肯兹写了一下午的 SQL ,死抠了各种业务细节,精简了各类逻辑表达,自认为方方面面都考虑周全,无可挑剔。
虽然用了二十多个 UNION ALL, 代码长达 800 多行,但整体代码排版合理,逻辑清晰可见,一是一,二是二,阅读体验特别棒。这么完美的一个报表 SQL,自己看着都要给自己磕头。
但,就怕人比人!直到我看到另一个同事写的SQL,区区2,30行,结果居然一样的。便羞耻得惊掉下巴。怎么会这样?!
我忍不住从 Code Repo 里面 Clone 下来,仔细把玩,哦不,品读。
细看,这段SQL,版面清洁光滑,短小耐看,逻辑还不失完整。我不由得连连佩服,这样清秀的代码,简直把我摁在地上,摩擦了几条街。
从此,我便开始注意代码的凝练,就像写作般克制。于是就有了那篇《如何写好 5000 行的 SQL 代码》。
总体来说,写 SQL 或者其他代码,反复修改或重构,是提升自己的不二之法。
自那以后,我放弃了一遍就写好代码的妄想,刻意在每次写完之后,都反复修改 2-3 遍,直到自己心里说“ 对了,就是这样!”,才敢签入代码库。
就像现在我写文一样,越是害怕,越是难以下笔。唯有鼓足勇气,多读,多修改,内心的纠结与痛苦,才得以缓解。
你猜对了,我为最近的难产,找到一个好借口!
如此小心翼翼,却始终也还担心,再次遇到这位朋友,恐怕他的造诣又上升了几个段位。
有时,真被自己见贤思齐的心态,折腾得够呛。梦回午夜,经常感叹,自己的智商,技艺如此之低,竞争力何在啊。
扯远了,拉回到那 3对2组的 SQL 上来!
组之间,完成的是 A ∪ B 与 B∪A的 转换。所以他们之间并没有不同。但“对之间”,差异就很大。
这就是 SQL 等价改写的魅力所在!
运行第一对,看其执行计划:
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男' OR STUDENT_GENDER=N'女'
Table Scan 这个物理操作,代表的是访问表的方式。在这里,Table Scan 执行了全表扫描的操作。
Table Scan 这是一个非常危险的操作,需要优化
运行第二对,它的执行计划是这样:
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男'UNION SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER=N'女'
除了第一对里出现的 Table Scan, 这里还出现了 Sort(Distinct Sort) 和 Concatenation 操作符。
这两个操作符,是拜 UNION 所赐,UNION 有一层去重的功能。所以,它的这个功能在本次查询中,是多余的,可去除。
第三对:
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男'UNION ALL SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER=N'女'
比起第二对,UNION ALL 去掉了去重的功能,即上下两个结果集,如果有同样的一条记录,会在最终的结果集保留下来
虽然,UNION ALL 会在性能上,优于 OR, 需要小心的是,在这里 A 与B 的限制条件互斥,才能改写,一旦两者有重合,则会出现重复记录,这就与实际需求不符了。
比如,往 STUDENTS 表里新建一条未知性别的同学:
INSERT INTO dbo.STUDENTS(STUDENT_NAME,STUDENT_GENDER)
SELECT 'Test Case' , 'UNKWN' AS STUDENT_GENDER
再执行
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'男' OR STUDENT_GENDER = N'UNKWN'
UNION ALL
SELECT *
FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'女' OR STUDENT_GENDER = N'UNKWN'
ORDER BY STUDENT_GENDER
就能看到有两条 UNKWN 性别的记录;
STUDENT_ID STUDENT_NAME STUDENT_GENDER
33815 Test Case UNKWN
33815 Test Case UNKWN
所以,SQL 转换前提,一定是等价.
上面的例子,是日常开发或面试常见操作。底下这例,便是体现优化功底的骚操作,不曾用过,就真不知道还能这么干。
SELECT *
FROM (SELECT A.*, B.*, C.*, D.*, E.*, F.*FROM A INNER JOIN B ON B.XXX = A.XXXINNER JOIN C ON C.ZZZ = B.ZZZ INNER JOIN D ON D.YYY = C.YYY INNER JOIN E ON E.III = E.IIIINNER JOIN F ON F.PPP = E.PPP
) TMP LEFT JOIN G ON G.WWW = TMP.WWW
WHERE TMP.FLD1 = 'SAMSUNG'AND TMP.FLD2 = 'KOREA'AND TMP.FLDX ='XXXX'
这种多表连接的 SQL,司空见惯。恐怕连接的表,只有更多。
初学者,往往能将逻辑理清楚,就已经非常吃力了。就像我之前的例子,哗哗哗,一通写下来,把数据找正确,就满足了。
但,假如 FLD1, FLD2, FLDX,隶属于 A,B,C,D,E,F,你是否能看出点什么来?
没错, A ∩ B = B ∩ A 交集等价转换:
SELECT *
FROM (SELECT A.*, B.*, C.*, D.*, E.*, F.*FROM A INNER JOIN B ON B.XXX = A.XXXWHERE A.FLD1 = 'SAMSUNG'AND A.FLD2 = 'KOREA'AND B.FLDX ='XXXX'
) TMP INNER JOIN C ON C.ZZZ = B.ZZZ INNER JOIN D ON D.YYY = C.YYY INNER JOIN E ON E.III = E.IIIINNER JOIN F ON F.PPP = E.PPPLEFT JOIN G ON G.WWW = TMP.WWW
前提:FLD1, FLD2, FLDX 隶属于 A,B 两表,且不是计算字段
原先的内连接,会抛出一个巨大的矩阵:
SELECT A.*, B.*, C.*, D.*, E.*, F.*FROM A INNER JOIN B ON B.XXX = A.XXXINNER JOIN C ON C.ZZZ = B.ZZZ INNER JOIN D ON D.YYY = C.YYY INNER JOIN E ON E.III = E.IIIINNER JOIN F ON F.PPP = E.PPP
而事实上,基于
WHERE A.FLD1 = 'SAMSUNG'AND A.FLD2 = 'KOREA'AND B.FLDX ='XXXX'
这样的条件,只能选出一条或者少量数据。那前期做了很多 Join 操作,就变成了无用功,浪费了计算资源。
驱动表最小化,这是优化的一条方法。如果优化器,做不到谓词推进,那只能人工帮他做选择。
什么是“谓词推进”?
当在 STUDENTS 表上加索引后,
CREATE INDEX IDX_STU_GENDER ON dbo.STUDENTS(STUDENT_GENDER)
执行查询:
SELECT STUDENT_GENDER FROM dbo.STUDENTS
WHERE STUDENT_GENDER = N'UNKWN'
标记为红框的部分,就是谓词表达式。只有谓词靠近原表,才能发挥减少数据访问量的作用。
--完--
往期精彩:
本号精华合集(三)
外企一道 SQL 面试题,刷掉 494 名候选人
我在面试数据库工程师候选人时,常问的一些题
零基础 SQL 数据库小白,从入门到精通的学习路线与书单
基于关系型代数的 SQL 等价改写相关推荐
- 10年+SQL性能优化专家谈SQL等价改写核心思想
墨墨导读:2020数据技术嘉年华于11月21日落下帷幕,大会历时两天,来自全国各地的数据领域学术精英.领袖人物.技术专家.从业者和技术爱好者相聚北京,见证了个人的快速成长.技术的迭代进步.行业的蓬勃发 ...
- 资源放送丨《SQL条件等价改写秘笈》PPT视频
点击上方"蓝字" 关注我们,享更多干货! 前段时间,墨天轮邀请数据库资深专家 怀晓明 老师分享了<SQL条件等价改写秘笈>,在这里我们将课件PPT和实况录像分享出来,供 ...
- 15日直播预告丨SQL条件等价改写秘笈(主讲人:怀晓明)
经典知识库:SQL条件等价改写秘笈 -7月15日20:00 如果你有一定SQL编写及优化经验,那么SQL等价改写很可能会是你了解的一个技能领域.根据实际从业情况,你或多或少,都会知道一些SQL等价改写 ...
- 从一个简单的SQL来聊聊等价改写
今天看到一个库的cpu告警,去看了下top sql就发现了一个奇葩的SQL. 不得不吐槽下,都2022年了,还有在索引列上用函数的写法.. 其实也很好理解这个SQL是希望查询时间列是当天(curren ...
- sql优化第一天,认识优化器和RBO中的等价改写SQL例子1
oracle有两种优化器,一种是基于成本的优化器CBO,一种是基于规则的优化器RBO,早期的版本使用基于规则的RBO优化器.从oracle10 g开始起,CBO已经成为比较成熟的优化器,但是要对sql ...
- 《机器学习与数据科学(基于R的统计学习方法)》——2.11 R中的SQL等价表述...
本节书摘来异步社区<机器学习与数据科学(基于R的统计学习方法)>一书中的第2章,第2.11节,作者:[美]Daniel D. Gutierrez(古铁雷斯),更多章节内容可以访问云栖社区& ...
- 第十二章 动手,经典等价改写让SQL飞
参考<收获,不止SQL优化>作者: 梁敬彬 / 梁敬弘 SQL优化的本质:减少访问路径 一. 等价改写思维导图
- 基于oracle的sql优化
[基于oracle的sql优化] 基于oracle的sql优化 [博主]高瑞林 [博客地址]http://www.cnblogs.com/grl214 一.编写初衷描述 在应有系统开发初期,由于数据库 ...
- 崔华 oracle简历,2013数据库大会:崔华-基于Oracle的SQL优化案例分析
2013数据库大会:崔华-基于Oracle的SQL优化案例分析 崔华的新书即将出版,其数据库大会上的演讲也非常精彩,他的新书十分值得期待. 2013年中国数据库技术大会第二天的"Oracle ...
最新文章
- java中action请求_java模拟js发送action请求
- 深入理解printf 之一 问题引出
- linux 32bit swt,无法在Windows 32位上加载SWT库
- JavaSE02、方法,递归迭代
- 1c:\program files\microsoft visual studio 9.0\vc\atlmfc\include\afx.h(24) : fatal error C1189: #err
- PX4 vision_to_mavros定位
- 15个最好的Bootstrap设计工具推荐
- 关于Redis的常见面试题解析
- 华为s5700交换机使用配置
- python mobi_使用Python爬取mobi格式电纸书
- 《Java SE编程365例》003: 我的电子书城
- 网工必知 | 什么叫一层交换机,二层交换机,三层交换机?
- amd cpu 安卓模拟器_AMD的CPU如何运行安卓模拟器?
- 安装Altera USB-Blaster驱动程序遇到的问题
- 串口(RS - 232)
- 【2022-New】Flutter doctor 检测报错,Android toolchain - develop for Android devices
- UVA - 1600 Patrol Robot (巡逻机器人)(bfs)
- 在cmd中怎么进入mysql?
- java后端扇形图实现
- 【MindManager软件常用快捷键】Mindjet MindManager快捷键教程
热门文章
- Kafka之Controller(Broker的领导者)
- ANTLR学习(三)antlr的功能
- 【博学谷学习记录】超强总结,用心分享 | 产品经理必备技能之Axure RP9(持续更新)
- 红外夜视摄像头 小方智能摄像头使用手记
- iOS即时通讯,从入门到“放弃”?
- linux开放7050端口,Linux操作系统内核启动参数详细解析
- html中pre与xmp标签的区别是什么
- CNN卷积核计算原理
- 浏览器输入www.baidu.com之后发生了什么
- 谷歌如何注册账号?手机号无法验证处理方法!2023年最新教程!