一对多关联导致数据发散的SQL优化经验

工作中会经常写SQL,而且工作中查询的数据量,一般都是几百万,甚至几亿的数据量。如果自己的SQL写的很烂,就可能导致查询了很久也不会跑出结果来。而我在工作中写SQL经常遇见的一个问题就是一对多关联导致数据发散。因此,这篇文章,主要记录一对多关联导致数据发散的应对方案。

多表关联优化的例子

我之前写了一个SQL,运行了一个多小时,也没有运行出来。
下面我列出这个SQL的逻辑:

SELECT t1.active_date,
       t1.platform_name,
       t1.type,
       COUNT(DISTINCT t1.uid) AS user_cnt1,
    count(DISTINCT case when t2.uid is not null then t2.uid end) as user_cnt2,
     count(DISTINCT case when t3.uid is not null then t3.uid end) as user_cnt3
  FROM  table1 as t1
  LEFT JOIN (SELECT  uid,
                    active_date,
                    platform_name
               FROM  table2
              WHERE active_date = '2020-07-23') t2
    ON t1.fuid = t2.fuid
   AND  t1.active_date = t2.active_date
   AND t1.platform_name = t2.platform_name
  LEFT JOIN (SELECT 
     uid,
                    active_date,
                    platform_name
               FROM  table3
              WHERE active_date = '2020-07-23') t3
    ON t1.fuid = t3.fuid
   AND t1.f_p_date = t3.f_p_date
   AND t1.platform_name = t3.platform_name
 GROUP BY t1.active_date, t1.platform_name,t1.type;

其中,table1有100w的数据量;table2有200w的数据量,而且是分区表;table3有500w的数据量,也是分区表。
从SQL来看上面的逻辑,其实很清晰:就是一个主表t1left join两个表,主表的数据量为100w左右,left join的两个表分别为两个分区表,数据量分别是200w和500w。咋一看,这三个表join怎么也不会跑这么长的时间,那怎么会跑不出来结果呢?
根据经验来看,一个SQL有问题,无非就是两个情况:一个是SQL写的太烂,自己把自己搞死了;另一个是表的关联有问题。上面的SQL逻辑不难,而且SQL写法也没有什么可以优化的地方。对left join的两个表继续观察,才发现table2的数据是有重复的,这就导致table1和table2关联的条件也是有重复的。这也就是说,table1和table2他们之间的关联是一对多的关系,这样就会导致查询的数据量膨胀和发散。但如果是一对一的关系,join的数据量是不会膨胀的。
此时,我对table2的数据进行去重之后,再和table1关联,结果运行了几分钟,数据就出来了,性能提高了很多。
优化后的SQL

SELECT t1.active_date,
       t1.platform_name,
       t1.type,
       COUNT(DISTINCT t1.uid) AS user_cnt1,
    count(DISTINCT case when t2.uid is not null then t2.uid end) as user_cnt2,
     count(DISTINCT case when t3.uid is not null then t3.uid end) as user_cnt3
  FROM  table1 as t1
  LEFT JOIN (SELECT  uid,
                    active_date,
                    platform_name
               FROM  table2
              WHERE active_date = '2020-07-23'
              group by uid,
                    active_date,
                    platform_name   --table2数据去重
                    ) t2
    ON t1.fuid = t2.fuid
   AND  t1.active_date = t2.active_date
   AND t1.platform_name = t2.platform_name
  LEFT JOIN (SELECT 
     uid,
                    active_date,
                    platform_name
               FROM  table3
              WHERE active_date = '2020-07-23') t3
    ON t1.fuid = t3.fuid
   AND t1.f_p_date = t3.f_p_date
   AND t1.platform_name = t3.platform_name
 GROUP BY t1.active_date, t1.platform_name,t1.type;

SQL优化的经验

截止到目前,我自己在写SQL踩坑最多的地方,始终是多表关联导致数据发散这个问题。如果出现了该问题,我一般的思路是从以下几点来思考的:
1、表关联的条件对不对:工作中的SQL查询会用到很多表,很多表甚至一开始是陌生的,本来你以为两个表的关联条件是用户UID,实际上是订单ID,or用户UID+日期 or用户UID+其他条件。如果一开始表之间的关联条件不完整或者是错误的,这就会导致数据发散的现象。因此,我们一开始要确定好表之间关联的正确逻辑。
2、表的数据是不是唯一的:如果确定了表关联的条件无误,但某个关联表中的数据有重复,这也会出现一对多关联的情况。这个时候,可以把有重复数据的表先group by,再去关联。

除此之外,在多表关联查询的时候,还有两个经验想分享给大家:
1、多表关联的时候,尽量遵循“小表在前,大表在后”的原则,一般来说,这样查询效率会更高一点;
2、SELECT子句中避免使用 ‘ * ‘,SQL在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。反而,在select查询时 ,枚举出自己需要的表中的那些字段,会比使用*查询效率高。

猜你喜欢

转载自blog.csdn.net/Monica114/article/details/107604903