postgresql实现多表关联删除性能分析

转载自:
https://mp.weixin.qq.com/s?src=11&timestamp=1628513358&ver=3242&signature=s26j91cKXj5oZohocJhY7DRGiRgSnzIvowxS9OymsLiHXjUfK7JZihyGpLlHVUYmdQk9pJfAwx4WZ0ThI8zewPLF8ItKTCO7V4wbLSNhgEzlDjqssW2JooLFLkHxKR&new=1

导读:最近在进行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;

执行计划如下:
在这里插入图片描述

可以发现,在关联删除时,预计删除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
);

在这里插入图片描述
可以发现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的问题,因篇幅有限,将在后续的文章中介绍到。

<think>好的,用户想在PostgreSQL中通过JOIN创建关联的视图。首先,我需要回忆一下创建视图的基本语法。根据引用[1]中的内容,创建物化视图使用的是CREATE MATERIALIZED VIEW,但用户可能指的是普通视图,所以应该用CREATE VIEW。然后,关联通常涉及到JOIN操作,比如INNER JOIN或者LEFT JOIN。 接下来,我需要考虑如何将JOIN语句整合到视图的创建中。例如,用户可能有两个,比如t1和t2,他们想通过某个字段连接,比如id。假设用户有两个t1和t2,通过id关联,那么视图的查询部分应该是SELECT ... FROM t1 JOIN t2 ON t1.id = t2.id。这部分需要放在CREATE VIEW语句的AS后面。 另外,用户可能会遇到是否需要使用物化视图的问题。物化视图会存储实际数据,而普通视图只是保存查询语句。根据引用[1],创建物化视图时可以选择WITH DATA或WITH NO DATA,但普通视图不需要这个选项。因此,用户如果只是需要动态查询,应该使用普通视图。 然后,可能需要提醒用户关于视图的维护,比如当基础结构变化时,视图可能需要重新创建。引用[2]中提到,PostgreSQL中修改视图通常需要删除并重新创建,所以用户如果需要修改视图定义,应该使用DROP VIEW再CREATE VIEW,或者使用CREATE OR REPLACE VIEW。 另外,如果用户需要更复杂的处理,比如窗口函数或CTE,可以引用[3]中的内容,说明PostgreSQL支持这些高级功能,可以在视图中使用。 最后,可能需要提到性能优化,比如查看查询计划(引用[4]和[5]),确保视图的查询效率,是否使用了索引等。 总结步骤应该是: 1. 使用CREATE VIEW语句,定义视图名称。 2. 在AS子句后编写JOIN查询,连接相关。 3. 执行创建后,可以像查询普通一样使用视图。 4. 需要修改时,使用CREATE OR REPLACE VIEW或者删除后重建。 5. 考虑性能优化,比如索引或查询计划分析。 同时,用户可能还想知道如何更新视图或者处理数据变更,这时候可能需要解释普通视图和物化视图的区别,以及WITH DATA的作用。例如,物化视图存储实际数据,需要手动刷新,而普通视图每次查询时动态生成结果。 可能还需要给出一个具体的例子,假设用户有t1和t2,通过id关联,创建视图的SQL语句示例,并说明如何查询该视图。此外,提醒注意事项,比如权限问题,视图依赖的结构变化的影响等。</think>在 PostgreSQL 中创建关联的视图可以通过 `CREATE VIEW` 语句结合 `JOIN` 操作实现。以下是具体步骤和示例: --- ### **1. 基础语法** 使用 `CREATE VIEW` 定义视图,并在 `AS` 子句中编写包含 `JOIN` 的查询语句: ```sql CREATE VIEW view_name AS SELECT column1, column2, ... FROM table1 JOIN table2 ON table1.key = table2.key; ``` --- ### **2. 示例场景** 假设有两个 `t1`(主)和 `t2`(子),通过 `id` 字段关联: ```sql -- 创建基础 CREATE TABLE t1 (id INT PRIMARY KEY, name TEXT); CREATE TABLE t2 (id INT REFERENCES t1(id), value INT); -- 创建关联视图 CREATE VIEW t1_t2_view AS SELECT t1.id, t1.name, t2.value FROM t1 INNER JOIN t2 ON t1.id = t2.id; ``` --- ### **3. 使用视图** 创建后,可以像查询普通一样使用视图: ```sql SELECT * FROM t1_t2_view WHERE value > 100; ``` --- ### **4. 修改视图** 如果需要调整视图定义,需使用 `CREATE OR REPLACE VIEW`(注意字段数量和类型需兼容): ```sql CREATE OR REPLACE VIEW t1_t2_view AS SELECT t1.id, t1.name, t2.value, t2.created_at FROM t1 LEFT JOIN t2 ON t1.id = t2.id; -- 改为左连接 ``` --- ### **5. 物化视图(可选)** 若需要缓存结果(适用于数据更新频率低的场景),可使用 `CREATE MATERIALIZED VIEW`[^1]: ```sql CREATE MATERIALIZED VIEW t1_t2_mview AS SELECT t1.id, t1.name, t2.value FROM t1 JOIN t2 ON t1.id = t2.id WITH DATA; -- 立即加载数据 ``` --- ### **注意事项** - 视图不存储实际数据,每次查询动态生成结果。 - 若基础结构变更,需重建视图[^2]。 - 复杂场景可结合窗口函数或 CTE 优化[^3]。 - 通过 `EXPLAIN` 分析查询计划以优化性能[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值