暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

面试现场:join联表的注意点、有哪些联表查询方式、真题...

Echo1024 2021-06-02
1307



《为研发同学定制的MySQL面试指南》 
贯穿单机、集群、数据库中间件!
面试官都关注了!你还犹豫?

Hi,大家好!我是白日梦!

今天我要跟你分享的MySQL话题是:“join联表的注意点、有哪些联表查询方式、真题...”

本文是MySQL专题的第 34 篇。

欢迎关注!持续更新中~



你好,看你简历中有写熟悉MySQL数据库啊,要不咱聊聊数据库?


嗯,好啊!




说一下常见的联表查询方式吧


嗯~ 如下:  

主要有内连接、外连接、交叉连接

内连接又包括:等值连接、非等值连接、自连接


外连接又含有:左外连接、右外连接、全外连接                      




嗯!那我现在有这样的两张表。

    create table `t1`(
    `id` int(11) NOT NULL,
    `a` int(11) default null,
    `b` int(11) default null,
    primary key (`id`),
    key `a` (`a`)
    )engine=innoDB;


    create table `t2`(
    `id` int(11) NOT NULL,
    `a` int(11) default null,
    `b` int(11) default null,
    primary key (`id`),
    key `a` (`a`)
    )engine=innoDB;

     其中表t1中有100行数据,表t2中有1000行数据。



    你能说一下表t1和t2在进行join操作时,是t1 join t2复杂度更低呢?还是t2 join t1复杂度更低呢?


    嗯嗯,具体是小表join大表,或者是谁join谁都无所谓是要根据具体的SQL以及join buffer具体的分析的!



    比如我们的SQL是这样的

      select * 
      from t1 staight_join t2
      where t1.a = t2.b

      这条SQL的where条件中使用的b列并不存在任何索引。那看起来,貌似这条SQL的执行逻辑应该像下面这样:


      Step1:依次扫描t1表中的每一行rx。

      Step2:取出rx中的b列的值去t2表中进行全表扫描。

      Step3:找到t2表中符合条件的row,和现有的数据组合作为结果

      Step4:重复1~3,直到t1中的数据被遍历一遍。


      这样看起来无论是t1 join t2,还是t2 join t1都避免不了在遍历t1的同时也遍历t2,或者在遍历t2的时候遍历t1。最终都要扫描100*1000次磁盘。


      But,MySQL其实没有这么做!




      MySQL的做法如下:


      Step1:扫描驱动表t1全表,将全部的100行数据加载进内存join buffer中。假设t1中有N条数据,那么此次扫描时间复杂度就是N


      Step2:依次扫描被驱动t2表(假设有M行)中的每一行,将得到的每一行和join buffer 中的数据行做比较,比较的条件就是where语句中的条件。此次扫描时间复杂度为M


      Step3:合并符合条件的数据,作为结果。

      Step4:重复1~3,直到t1中的数据被遍历一遍。


      还是假设驱动表中有N行,被驱动表中有M行。这样看起来其实扫描数据库的次数不再是N*M或者M*N而是N+M!


      当然还多出了在内存中进行的N*M次使用where条件进行的比较。





      嗯。还有其他情况嘛?


      嗯,还有如果我们的SQL join条件如下

        select * 
        from t1 staight_join t2
        where t1.a = t2.a

        那这条SQL的执行条件如下:


        Step1:遍历表t1,依次从t1中取出一行数据rx


        Step2:从rx中取出列的值,使用这个值去t2表的a列这棵二级索引树中进行检索。B+Tree的查询时间复杂度是:log2(M)


        Step3:拿着Step2中获取到的id列在t2中进行回表,id也有对应的B+Tree,这次查询的时间复杂度也是log2(M),将查询到的结果组合作为返回值。


        Step4:重复1~3


        因为这条SQL检索t2时走了索引,所以时间复杂度肯定比上一条SQL更好。


        t1肯定是走了全表扫描:100行

        t2每次需要两次在B+Tree中检索:2*log2(M)


        所以整体的时间复杂度是:N+2*log2(M)


        所以在这种情况下,N对整体复杂度的影响最大!所以还是小表驱动大表要更高效一些。




        嗯! 是的。


        另外刚才有说到的join buffer这个缓存。它里面会存储的驱动表中的数据。默认大小是256k。


        比如我们上面的SQL语句是select * from,那么t1中的数据会被全部加载进join buffer中。




        嗯! 那问题来了,如果驱动表t1中的数据太多了,join buffer一次性存储不下这么大的数据怎么半?


        嗯,针对这种情况:解决的策略就是将t1表中的数据分段、分别加载进join buffer中。


        比如t1中的100行数据被分成了80行、20行两段分别放入join buffer中进行上面说过的Step操作,但是整个过程中使用where条件比较的次数 (80+20)*1000 是不变的...




        嗯! 你继续说


        回过头来重新看这个问题,在join buffer不够大时,假设驱动表数据行数为N,需要分成K段,被驱动表有数据行数M。 


        所以这种分段的情况下,扫描磁盘的次数不再是N+M

        而是:N+K*M



        得到上面的公式后,我们假设join buffer的大小就是默认的大小256k,t1的大小是2560k,t2的大小是25600k


        所以对于t1做驱动表来说:K = 2560/256 = 10段

        需要扫描到总条数为:N+K*M

        也就是 100+10*1000 = 10100


        对于t2做驱动表来说:K = 25600/256 = 100段

        需要扫描到总条数为:N+K*M

        也就是 1000+100*100 = 11000


        所以当join buffer不足够大时,选择小表当作驱动表看起来复杂度要更友好一些。




        可!  总结来说就是当join buffer足够大且驱动表没有走索引时,选谁当驱动表复杂度都是一样的,当join buffer小时,选择小表当驱动表复杂度会更低一些。


        但是无论如何,都是join buffer越大越好


        嗯嗯,是的!另外还有这样一个参数:join_buffer_size 可以控制join buffer的大小。

          mysql> show variables like '%join_buffer_size%';
          +------------------+---------+
          | Variable_name | Value |
          +------------------+---------+
          | join_buffer_size | 8388608 |
          +------------------+---------+
          1 row in set (0.08 sec)



          回答的还可,开始下一个话题吧!我有两种表如下:              

            mysql> select * from t1;
            +----+------+
            | id | name |
            +----+------+
            | 1 | a |
            | 1 | b |
            | 2 | c |
            | 2 | d |
            +----+------+
            4 rows in set (0.00 sec)


            mysql> select * from t2;
            +----+------+
            | id | name |
            +----+------+
            | 1 | e |
            | 2 | f |
            | 3 | g |
            | 4 | h |
            +----+------+
            4 rows in set (0.00 sec)

            你看下我这条SQL的返回值是啥?

              select * 
              from t1 , t2 
              where t1.id = t2.id;

              好(内心:这泥马



                mysql> select * from t1,t2 where t1.id=t2.id;
                +----+------+----+------+
                | id | name | id | name |
                +----+------+----+------+
                |  1  | a   |  1 | e    |
                | 1 | b | 1 | e |
                |  2  | c   |  2 | f    |
                |  2 | d   |  2 | f    |
                +----+------+----+------+
                4 rows in set (0.00 sec)

                难是不难,你有没有被唬住?




                嗯,好没有其他问题了,你表现还可以,应该会有下一面的,你还有想问我的嘛?


                没有问题了!感谢您安排的面试



                欢迎关注:

                参考:

                《丁奇大佬的MySQL45讲》


                推荐阅读:

                1、MySQL的修仙之路,图文谈谈如何学MySQL、如何进阶!

                2、数据库面经,常见的面试题

                3、谈谈MySQL中基数是什么?

                4、聊聊什么是慢查?如何监控?如何排查?

                5、对Not Null字段插入Null值有啥现象?

                6、能谈谈year、date、datetime、time、timestamp的区别吗?

                7、你有没有搞混查询缓存和Buffer Pool?谈谈看!

                8、你知道数据库缓冲池中的LRU-List吗?

                9、了解InnoDB的FreeList吗?谈谈看!

                10、了解Flush-List吗?顺便说一下脏页的落盘机制!

                11、用 11 张图讲清楚,当你CRUD时BufferPool中发生了什么!以及BufferPool的优化!

                12、了解 MySQL的表空间 和 数据表吗?谈谈看!

                13、了解 MySQL的数据行吗?行溢出机制呢?谈谈看!

                14、了解MySQL数据页吗?说说什么是页分裂吧!

                15、用一分钟了解fsync这个系统调用

                16、简述undo log、truncate、以及undo log如何帮你回滚事务?

                17、我劝!这位年轻人不讲MVCC,耗子尾汁!

                18、传说中的MySQL的redo log是什么?谈谈看!

                19、LSN、Checkpoint?谈谈MYSQL的崩溃恢复是怎么回事!

                20、MySQL的 bin log有啥用?在哪里?谁写的?怎么配置?

                21、bin log有哪些格式?有啥区别?优缺点?线上用哪种格式?

                22、删库后!除了跑路还能干嘛?

                23、全网最牛X的!MySQL两阶段提交串讲!没有之一!

                24、自导自演的数据库面试现场--谈谈MySQL的10种文件

                25、大型面试现场:一条update sql的执行都经历了什么?

                26、大型翻车现场:如何实现记录存在的话就更新,如果记录不存在的话就插入。

                27、如何实现记录存在的话就更新,如果记录不存在的话就插入--续

                28、面试现场:说说char和varchar的区别你了解多少?

                29、沙尘暴也阻挡不了学习的脚步-- 面试官:你竟然不知道MySQL的组提交?

                30、聊聊MySQL大insert buffer和change buffer吧!

                31、一看就懂的MySQL的double write

                32、简述3大范式

                33、各种登陆MySQL的方式

                长按二维码、关注白日梦

                赠送精美脑图(操作系统、网络、Java基础...)

                长按二维码

                关注白日梦吧!


                文章转载自Echo1024,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论