电子说
作者:京东物流 向往
一、背景
从事数据开发将近四年,过程中有大量任务交接或阅读同事代码的场景。在这些场景中发现有些SQL读起来赏心悦目,可以一目了然地了解业务逻辑,一些复杂的业务需求实现方法也可以做到简洁优雅,同时在性能上也有良好表现。而有些SQL读起来非常艰难,时常要跨越几百行寻找WHERE条件或者关联字段,甚至充斥着大量相同的子查询命名,除了作者可能少有人能快速看懂。
为此,基于个人经验、理解与实践,我总结了一些方法和技巧,能让SQL尽量变得优雅,即兼顾代码可读性和执行性能两方面的提升。
二、方法与技巧
1.子查询与谓词下推
很多同事在写关联逻辑时,习惯于直接将原表关联,随后在最下方用一大段WHERE语句进行条件过滤,如下示例:
// -------------------- Bad Codes ------------------------ SELECT f1.pin, c1.site_id, c2.site_name FROM fdm.fdm1 AS f1 LEFT JOIN cdm.cdm1 AS c1 ON f1.erp = lower(c1.account_number) LEFT JOIN cdm.cdm2 AS c2 ON c1.site_id = c2.site_code WHERE f1.start_date <= '""" + start_date + """' AND f1.end_date > '""" + start_date + """' AND f1.status = 1 AND c1.dt = '""" + start_date + """' AND c2.yn = 1 GROUP BY f1.pin, c1.site_id, c2.site_name
这段SQL主要有两个问题:
1.cdm1和cdm2的条件写在LEFT JOIN之后,因为cdm1和cdm2是NULL补充表(NULL 补充表: 右表被称为 NULL 补充表,意味着它的存在是为了补充左表中可能缺失的值。即使在右表中没有与左表匹配的行,左表中的行仍然会被返回,右表的相关列会填充为 NULL),那么19和20行无法进行谓词下推,这会导致关联时fdm1和cdm1,cdm2先进行全表关联,再按照WHERE条件过滤分区。如果cdm1是每天全量的表,先关联全表所扫描的数据量可想而知是相当大的。
2.全表关联时没有对关联键进行NULL值处理,如果相关表的对应字段存在大量NULL值,会引起数据倾斜。
第一个问题涉及SQL的谓词下推,即写条件时,应该在不影响结果的情况下,尽量将过滤条件下推到join之前进行(“下推”指将条件推到靠近数据源的位置而不是SQL语句的方位)。谓词下推后,过滤条件在map端执行,减少了map端的输出,降低了数据在集群上传输的量,节约了集群的资源,也可以提升任务的性能。
对于常用的INNER JOIN和LEFT OUTER JOIN,谓词下推规则如下:
INNER JOIN | LEFT OUTER JOIN | |||
---|---|---|---|---|
左表 | 右表 | 左表 | 右表 | |
ON条件 | 下推 | 下推 | 不下推 | 下推 |
WHERE条件 | 下推 | 下推 | 下推 | 不下推 |
如果使用上述示例的写法,主要关注的是LEFT OUTER JOIN时WHERE语句里的条件是否会引起谓词不下推。如果不想记这些看起来很复杂的规则怎么办?可以如下所示直接使用子查询:
// -------------------- Good Codes 审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !