点击蓝色“有关SQL”关注我哟
加个“星标”,天天与8000人一起快乐成长
这是本系列第二篇。
点击下面链接,可以阅读第一篇:
可以拿来吊打面试官的 SQL Join (一)
我在这个系列中,所分享的知识,力求逻辑严谨,实战辅证。但一如所有的文章一样,读者需要自己思考,是否正确无误,是否可以拿来直接作用于生产环境。对于没有理解透彻,就直接用于生产而造成的损失,本人概不负责。
不仅仅是看我的文章,看所有的文章,书籍或者教程,读者朋友们都该用自己的思维,带着批判的眼光,谨慎对待作者发表的观点,切勿盲目轻信。
回到正题,上一讲中,文末留了个预告,这回要讲 WHERE 和 ON 中,使用条件判断,哪个速度更快。
其实,对于数据库玩家来说,这是个很大的话题。讲明白这里面的道道,我们需要对很多限制条件做假设。或许太过于学术,枯燥和繁琐,但要让大家都明白,不得不这么做,所以才开这个系列。
刚才我说有很多限制条件要做假设,其实就为了公平的去衡量两者的执行性能。不能在 WHERE 中故意不加索引,或者故意在 ON 的条件上,加函数表达式。这样比起来就毫无意义。
比如拿第一篇文章中的例子来说:
SELECT Header.OrderId
, Detail.ProductName
, Detail.Amount
FROM tblOrderHeader Header
LEFT JOIN tblOrderDetail Detail
ON Header.OrderId = Detail.OrderId
AND Detail.Amount > 1000
WHERE Header.OrderDate > '2020-01-01'
事实上,我们应该假设,使用了 INNER JOIN. 在上篇文章中,有读者就提到 INNER JOIN 的情况下, 筛选表达式放在 WHERE 和 ON , 功能类似。所以这里我们就先讨论 INNER JOIN的情况。
将例子改成 INNER JOIN:
SELECT Header.OrderId
, Detail.ProductName
, Detail.Amount
FROM tblOrderHeader Header
INNER JOIN tblOrderDetail Detail
ON Header.OrderId = Detail.OrderId
AND Detail.Amount > 1000
WHERE Header.OrderDate > '2020-01-01'
此时,比较 Detail.Amount > 1000 放在 WHERE 中:
SELECT Header.OrderId
, Detail.ProductName
, Detail.Amount
FROM tblOrderHeader Header
INNER JOIN tblOrderDetail Detail
ON Header.OrderId = Detail.OrderId
WHERE Header.OrderDate > '2020-01-01'
AND Detail.Amount > 1000
这样比较,就满足了假设条件公平。
那什么样的情况,假设条件是不公平的呢,比如放在 WHERE 中,我们把 Detail.Amount > 1000 写成了 Detail.Amount - 1000 > 0 这就是不公平。
Amount> 1000 和 Amount - 1000 > 0 看起来一样,那么为什么就不公平了呢?稍有经验的朋友,就知道,在字段前加了函数的判断,是不能有效利用索引的。
而 Amount - 1000 相当于加了函数:
f(x)= x - 1000
这里要注意,有效利用索引和不走索引,完全是两码事。索引扫描 (Index Scan )和 索引搜索 (Index Seek)两者的性能截然不同。我们常说的索引,是平衡树(B-Tree)结构。Index Scan 会遍历这棵树,假如树上有 10W+ 叶子,也要全部访问一遍。但 Index Seek 是搜索树的叶子,每个树节点都记录着它叶子的数量还有编号,seek 的时候访问节点,就知道有没有需要的叶子,此时性能必然快。
imageWindows画图画的,不要嫌丑啊
索引的结构类似这样,root page 记录了索引 Amount 的范围. Amount 在500以下的,归档在 Node 1 节点上,而500以上,1000以下的,归档在 Node 2 上。
Node 1 共有 5 条记录, Amount 在 1 到 200 之间的记录是 Node 1 的第一条记录;Amount 在 201 到 300 之间的记录,是 Node 1 的第二条记录,以此类推。
(注意:Node 1, root page, Node 2 都是数据页,索引的数据页。)
其中 Node 1 的第一条记录,保存的是 Amount 范围,即 在1 到 200,随之保存的还有表中记录的地址指针,以此地址指针可以回溯表记录的其他信息。这一过程,称之为回表
这就是索引的基本数据结构与信息。掌握了这个知识点后,我们看 index scan 与 index seek 的区别。scan 是把 Node 1 和 Node 2 上的所有数据都扫描了一遍,而 seek 则很快可以定位到,比如 Amount = 756 是在 Node 2 上,从而省却对 Node 1的访问,性能就快了。
实际操作中,索引的数据页要比这里多得多,记录的信息也复杂的多。但这幅图足够说明索引的益处。
好,今天的分享暂时到这里。这是个系列,所以不适合一文讲太多,消化下再继续。下文会讲 WHERE 和 ON 时,对索引的访问方式,以及无索引时,访问效率如何提高。
敬请期待!
--完--
往期精彩:
本号精华合集(二)
如何写好 5000 行的 SQL 代码
如何提高阅读 SQL 源代码的快感
我在面试数据库工程师候选人时,常问的一些题
零基础 SQL 数据库小白,从入门到精通的学习路线与书单