美文网首页Java
从阿里手册引出的Join查询思考

从阿里手册引出的Join查询思考

作者: 程序花生 | 来源:发表于2020-09-17 14:54 被阅读0次

    join查询再熟悉不过了,看似没有必要把它拿出来说事,但某天一个学生问起了一个问题,有必要拿来说下。

    很多同学的毕生梦想都是想进BATJ的某家公司,其中又以案例为多数,是的,不管从哪个角度来说,阿里都是一个非常把棒的公司,值得去挑战一把。

    回到今天的话题,一个同学(阿里的铁杆粉),问了我一个问题,在实际工作中到底应不应该用join查询,到底应该注意点什么?

    别以为这个问题很傻,如果你关注过阿里的规范,里面就有显著的一条:

    【强制】超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。

    首先,我要交代,上面这句话出自《阿里java编程规范手册》,并不是我的原创,另外我想说的是,对于上面这句话,我不管你懂不懂为什么,请按手册里面说的,强制去执行。

    不过既然有同学再问,我认为还是有必要把这个问题拿来分析下

    1. 禁止超过3表的JOIN

    如果太多的表JOIN对性能的影响是非常大的(Join的性能以及注意事项我后面再分析),这个我相信,对于绝大多数读者都是认可的,起码能做到表面上理解,但也不排除某些另外情况,比如说...

    某天,有个同学小甲,给我发个SQL语句,一方面夸夸其谈他对公司的业务,表结构了解的如何透彻。

    我打开sql文件后,一个2132行的sqL语句映入眼帘,我心里顿时(&%%¥&……),好吧,我得承认这情况并不只一个,而且他们以这个为荣,我不知道当这同学离职后,后面接手的会不会和我一样的心情。

    现在我也无力反驳他,这可能在某些特殊的情况下对他是好的(谁敢开除他,谁敢接他的手),我现在只想聊聊出现过多表关联之后怎么来解决。

    确实答案也出自《阿里java编程规范手册》

    【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。

    其实虽然规范里面没说,但这是数据库设计里面非常重要的一条:

    反范式设计

    反范式化是针对范式化而言得,在前面介绍了数据库设计得范式

    所谓得反范式化就是为了性能和读取效率得考虑而适当得对数据库设计范式得要求进行违反

    允许存在少量的冗余,换句话来说反范式化就是使用空间来换取时间

    举一个简单的例子:

    selecta.cl1,a.cl2,a.cl3,b.cl4froma,bwherea.c1=b.c1

    上面这个sql语句查询的数据来自a,b两张表,仔细看你会发现,其中只有cl4这一列是出自b表,其他的所有都是a表,这样可以考虑建立一个c表,把c1l,cl2,cl3,cl4的数据全部关联起来,实际做的事情就是冗余了cl4这个字段。

    带来的好处呢? 查询的时候只要查询c表,效率不会提升才怪。

    2. 数据类型必须一致

    数据类型必须一致,请完全遵守,如果你定是要寻根究底钻牛角尖,那我们做个实验,做实验的前提是你要了解些许的执行计划,当然对索引也得一知半解才行。

    CREATETABLEstaffs (

    idint(11)NOTNULLAUTO_INCREMENT,

    namevarchar(24)NOTNULLDEFAULT''COMMENT'姓名',

    ageint(11)NOTNULLDEFAULT'0'COMMENT'年龄',

    posvarchar(20)NOTNULLDEFAULT''COMMENT'职位',

    add_timetimestampNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'入职时间',

    PRIMARYKEY(id),

    KEYidx_staffs_nameAgePos (name,age,pos)

    )ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf8COMMENT='员工记录表';

    INSERTINTOstaffsVALUES('1','2000','23','dev','2019-06-25 21:41:31');

    数据和表都准备好了后,我来写一个sql语句

    select*fromstaffswherename=2000

    很棒,结果也查询出来了,name是varchar类型, 而给出的2000是整型,这有什么问题吗?结果不是正确的吗?

    别着急,还记得前面创建了索引嘛?

    KEYidx_staffs_nameAgePos(name,age,pos)

    使用执行计划看下

    看出问题了吗?虽然我指定的name上面是索引的,但数据类型不一致,不好意思,结果是全表扫描

    如果数据类型一致呢?

    EXPLAIN

    select *fromstaffswherename ="2000"

    key有值,代表就是用到了索引,so,不需要再钻牛角尖了吧

    3. 保证被关联的字段需要有索引

    这句话很好理解吧,如果t1,t2表,如果sql语句

    select*fromt1straight_joint2on(t1.a=t2.a);

    你可能会问straight_join是什么? 他和join有什么区别呢?

    好吧,你既然问到了,我就还是说下,如果你使用join,Mysql优化器可能会选择t1或者t2来作为驱动表,而我使用的是straight_join,那么关系很明确了,t1是驱动表,那t2就是被驱动表。

    既然语句知道被驱动表了,那被关联的字段就是t2.a,按照规范的要求,必须在t2表的a列上必须创建有索引。

    t1是驱动表,我从t1表里面取出数据后,再去t2表查询

    如果t2.a有索引,我根据索引查询就好,查询了结果合并t1表的记录返回。

    如果t2.a没索引,那么就尴尬了,需对t1表里面的所有数据都需要在t2表进行一次全表扫描,如果t1是1000行数据,t2是10000,那得扫描多少行呢?结果是(t1行数*t2行数)1000万。

    这个时候我相信你也已经理解为什么被关联的字段需要有索引了吧。

    相关文章

      网友评论

        本文标题:从阿里手册引出的Join查询思考

        本文链接:https://www.haomeiwen.com/subject/alblyktx.html