可以拿来吊打面试官的 SQL Join (二)

点击蓝色“有关SQL”关注我哟

加个“星标”,天天与8000人一起快乐成长

可以拿来吊打面试官的 SQL Join (二)_第1张图片

这是本系列第二篇。

点击下面链接,可以阅读第一篇:

可以拿来吊打面试官的 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 的时候访问节点,就知道有没有需要的叶子,此时性能必然快。

可以拿来吊打面试官的 SQL Join (二)_第2张图片 image

Windows画图画的,不要嫌丑啊

索引的结构类似这样,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 数据库小白,从入门到精通的学习路线与书单

你可能感兴趣的:(可以拿来吊打面试官的 SQL Join (二))