背景
关于sql调参数、数据倾斜可以搜到很多文章,本文主要讲解常见的SQL开发场景、‘奇葩’SQL写法并深入执行计划,带你了解如何快速写出高效率SQL。
高效写法
union直接使用效率低吗?
场景介绍
在一些业务场景中,需要将多份数据合并在一起,比如要取客户信息,客户信息存在两张表中有交叉(假设两张表中交叉的客户信息是一致的),需先将两份数据合并在一起。
写法&执行计划探查
因为两张表中数据有交叉,所以需要会先将数据去重,然后再去join。去重方式常见于:
SELECT cst_id,cst_info FROM ( SELECT cst_id,cst_info FROM @cst_info_a WHERE dt = '${bizdate}' UNION SELECT cst_id,cst_info FROM cst_info_b WHERE dt = '${bizdate}' )cst_info ;
这种情况下,会理解为先将两两份数据不做任务处理就合并在一起,导致shuffle、中间临时写入的数据量和读取数据量和数据源都是一致的,然后再去做去重。因为数据量在中间过程没有没有减少,所以效率相对来说会低一些。现在来看一下执行计划:
发现执行计划是做过的优化的,已经是最优执行计划了。
接下来按照理解中的高效sql写法来看一下执行计划:
-- 方式一 SELECT cst_id,cst_info FROM ( SELECT cst_id,cst_info FROM @cst_info_a WHERE dt = '${bizdate}' GROUP BY cst_id,cst_info UNION SELECT cst_id,cst_info FROM @cst_info_b WHERE dt = '${bizdate}' GROUP BY cst_id,cst_info )cst_info; --方式二 SELECT cst_id,cst_info FROM ( SELECT cst_id,cst_info FROM @cst_info_a WHERE dt = '${bizdate}' GROUP BY cst_id,cst_info UNION ALL SELECT cst_id,cst_info FROM @cst_info_b WHERE dt = '${bizdate}' GROUP BY cst_id,cst_info )cst_info GROUP BY cst_id,cst_info;
两种写法的执行计划一致,如下:
发现自己另外加的聚合处理,反而增加了复杂度。
总结
ODPS已经对union做过优化,直接使用就可以了。并且对三个及以上的(X张)表做union,执行计划是X个MAP任务+1个REDUCE任务;不会像hive是X个MAP任务+(X-1)个REDUCE任务,还需要调整SQL才能实现最优的执行计划。
count distinct真的慢吗?
场景介绍
在开发过程中,经常会遇到一些数据探查,比如探查资产信息表中,有多少用户数,探查过程中经常会用到count distinct,那么它的效率如何?
写法&执行计划探查
探查资产信息表中近5天的用户数,常见的写法与常规认为的优化写法:
--选择近5天的资产来看 --常见写法,count distinct写法 SELECT COUNT(DISTINCT cst_id) AS cst_cnt FROM @pc_bill_bal WHERE dt BETWEEN '${bizdate-5}' AND '${bizdate}' ; --优化写法 SELECT COUNT(1) AS cst_cnt FROM ( SELECT cst_id FROM @pc_bill_bal WHERE dt BETWEEN '${bizdate-5}' AND '${bizdate}' GROUP BY cst_id )base ;
一般都会认为直接count distinct效率很低,是这样吗?接下来看一下两个执行计划对比
常规写法:
优化写法:
从执行计划可以看出,直接count distinct的写法被优化成了两次去重处理,一次计算总和,并不是直接全量来去重计算。再看优化写法,两次去重处理,两次计算总和,反而比count distinct多了一步,不过运行效率还是很快的。最后看一下运行时间和消耗资源,常规写法比优化写法快了28%(62s、86s),资源消耗少28%。