目录

使用delete using的写法

delete using多表关联删除性能分析

exists多表关联删除性能分析

总结

导读:最近在进行sql编写时,发现delete +using性能并不高, 这篇文章主要通过执行计划对多表关联删除操作进行性能分析,并给出另一种高性能exists关联删除的方法,具有很好的参考价值,希望对大家有所帮助。

        postgresql特有一种多表关联删除的写法,使用delete using语法。本人公司所使用的是Gauss DB分布式数据库,其基于postgresql内核打造,同样适用delete using语句进行多表关联删除,但此类语句在业务使用过程中发现其效率并不高。经过实验测试发现当外表的关联数据存在重复值时,效率下降明显。

使用delete using的写法

delete from test1 a using test2 b where a.id<b.id and a.name=b.name;

        using 有点类似子查询,可以关联包含在where子语句中的字段的表。本例子中的a.id<b.id就是筛选出a表中id小于b表中id的记录。

delete using多表关联删除性能分析

        我们对建立两张表test1和test2,其中test1为主表,test2为关联的外表。利用test2中的statistics_dt关联删除test1中的数据。

create table maple.test1 (statistics_dt date, id int) DISTRIBUTE BY HASH(id);
create TEMP table test2 (statistics_dt date, id int)  DISTRIBUTE BY HASH(id);

        向其中插入数据:

insert into maple.test1 values ('20210708',1);
insert into maple.test1 values ('20210708',2);
insert into maple.test1 values ('20210708',4);
insert into maple.test1 values ('20210708',5);
insert into maple.test1 values ('20210708',6);
insert into maple.test1 values ('20210709',1);
insert into maple.test1 values ('20210709',2);
insert into maple.test1 values ('20210709',3);
insert into test2 values ('20210708',1);
insert into test2 values ('20210708',2);
insert into test2 values ('20210708',3);

 我们使用insert into select的语法对将要删除的test1表进行数据翻倍处理:

insert into maple.test1 select * from maple.test1

多次执行后,test1的表的数据量到达2048条。为了确保执行计划的准确性,对两张表进行统计信息收集,并打印执行计划。

analyze test2;
analyze maple.test1;
explain delete from maple.test1 t1
using test2 t2
where 1=1 and t1.statistics_dt=t2.statistics_dt;

 执行计划如下:

 

sql server怎么关联删除 sql关联表删除_多表关联

         可以发现,在关联删除时,预计删除test1的数据数量为3840条,远远超过test1中原有的2048条数据。这是为什么呢?

        查询test1和test2的关联字段statistics_dt可以发现,test2中只有三条statistics_dt为20210708的数据,而test1的statistics_dt字段有20210708和20210709两个值,且经过我们多次插入后,两者数据量分别为1280和768。两者关联删除时,在statistics_dt字段为20210708的日期中出现笛卡尔积,刚好达到数据量为1280*3=3840条,导致cost值上升。在关联中出现笛卡尔积是一件非常可怕且难以预料的事情,尤其是分布式数据库中表的数据量常常是千万级,这更是一场可怕的灾难。下一节将介绍使用exists方式进行多表删除。

exists多表关联删除性能分析

先附上exists多表关联删除的写法及执行计划:

delete from maple.test1 t1
where exists( 
 select 1 from test2 t2
 where 1=1 and t1.statistics_dt=t2.statistics_dt
);

 

sql server怎么关联删除 sql关联表删除_执行计划_02

        可以发现cost从delete using的166.10下降到89.41,执行效率提升了接近一倍,是不是突然兴奋了起来。但是为什么同样的数据,不同的写法执行效率会差距这么大呢,这么长的执行计划该如何去看呢。没关系,我已经将重点用红圈画起来了。可以看到,exists的写法在对test1删除时,删除的数据量为1280,刚好对应test1中20210708的数据条数,并没有出现笛卡尔积。再往下翻阅执行计划时,出现了group by的语句,使用group by进行去重也是我们应对笛卡尔积的常用手法之一。

    看来原因找到了,使用exists语句进行多表关联时,首先会对外表进行group by操作,再进行关联操作,大大减少了关联的结果集,运行效率当然会快。(应该不会有人外表是大表,内表是小表进行关联吧)

     有人会问了,你这是test2表中出现了重复值造成的,如果test2表中没有重复值,两者的性能又如何呢?

    这次我们对于test2中插入没有重复值的数据,如下:

insert into test2 values ('20210708',1);
insert into test2 values ('20210709',2);
insert into test2 values ('202107010',3);

delete using执行计划

Streaming (type: GATHER)  (cost=1.92..109.52 rows=1 width=24)
  Node/s: All datanodes
  ->  Delete on test1 t1  (cost=1.11..104.61 rows=2048 width=24)
        ->  Streaming(type: REDISTRIBUTE)  (cost=1.11..52.61 rows=2048 width=24)
              Spawn on: All datanodes
              ->  Hash Join  (cost=1.11..52.20 rows=2048 width=24)
                    Hash Cond: (t1.statistics_dt = t2.statistics_dt)
                    Skew Join Optimized by Statistic
                    ->  Streaming(type: PART REDISTRIBUTE PART ROUNDROBIN)  (cost=0.00..50.92 rows=2048 width=18)
                          Spawn on: All datanodes
                          ->  Seq Scan on test1 t1  (cost=0.00..6.12 rows=2048 width=18)
                    ->  Hash  (cost=1.10..1.10 rows=156 width=10)
                          ->  Streaming(type: PART REDISTRIBUTE PART BROADCAST)  (cost=0.00..1.10 rows=3 width=10)
                                Spawn on: All datanodes
                                ->  Seq Scan on test2 t2  (cost=0.00..1.01 rows=3 width=10

 exists执行计划

Streaming (type: GATHER)  (cost=1.92..109.52 rows=1 width=24)
  Node/s: All datanodes
  ->  Delete on test1 t1  (cost=1.11..104.61 rows=2048 width=24)
        ->  Streaming(type: REDISTRIBUTE)  (cost=1.11..52.61 rows=2048 width=24)
              Spawn on: All datanodes
              ->  Hash Semi Join  (cost=1.11..52.20 rows=2048 width=24)
                    Hash Cond: (t1.statistics_dt = t2.statistics_dt)
                    Skew Join Optimized by Statistic
                    ->  Streaming(type: PART REDISTRIBUTE PART ROUNDROBIN)  (cost=0.00..50.92 rows=2048 width=18)
                          Spawn on: All datanodes
                          ->  Seq Scan on test1 t1  (cost=0.00..6.12 rows=2048 width=18)
                    ->  Hash  (cost=1.10..1.10 rows=156 width=10)
                          ->  Streaming(type: PART REDISTRIBUTE PART BROADCAST)  (cost=0.00..1.10 rows=3 width=10)
                                Spawn on: All datanodes
                                ->  Seq Scan on test2 t2  (cost=0.00..1.01 rows=3 width=10)

 可以看到,两者的执行计划中cost值相同,删除的数据条数也同为2048条。delete using的方法并没有出现笛卡尔积,而exists方法在Seq Scan 扫描后,也没有出现group by。执行计划中,只有第6行的hash join不同,也就说明两者在关联时,关联字段在外表中不存在重复值时,两者的执行效率是相同的。

总结

        在进行多表关联删除操作时,外表与内表相比,常常都是数据量很小的小表,采用exists关联删除的方法,可以在关联前对外表进行group by操作,有效避免因外表出现重复值而造成关联删除中出现笛卡尔积的现象。与delete using语句相比,exists关联删除性能更高,值得大家在业务场景中进行借鉴。

    在列存表的业务场景中,使用exists关联删除还可以避免一些因列存表存储机制,造成并发删除同一张表而引发error的问题