[英]Delete using CTE slower than using temp table in Postgres
我想知道是否有人可以解释为什么使用CTE而不是临时表可以运行这么长时间……我基本上是从客户表中删除重复信息(为什么存在重复信息超出了本文的范围)。
这是Postgres 9.5。
CTE版本是这样的:
with targets as
(
select
id,
row_number() over(partition by uuid order by created_date desc) as rn
from
customer
)
delete from
customer
where
id in
(
select
id
from
targets
where
rn > 1
);
我在运行了一个多小时后于今天早晨杀死了该版本。
临时表的版本是这样的:
create temp table
targets
as select
id,
row_number() over(partition by uuid order by created_date desc) as rn
from
customer;
delete from
customer
where
id in
(
select
id
from
targets
where
rn > 1
);
此版本完成大约7秒钟。
知道是什么原因造成的吗?
CTE速度较慢,因为它必须不更改地执行(通过CTE扫描)。
TFM(第7.8.2节)指出: WITH中的数据修改语句仅执行一次,并且始终执行至完成,而与主查询是否读取其所有(或实际上)任何输出无关。 请注意,这与WITH中的SELECT规则不同:如上一节所述,仅在主查询需要其输出时,才执行SELECT的执行。
因此这是一个优化障碍 ; 对于优化者,不允许拆除CTE,即使这样做会导致更明智的计划并获得相同的结果。
不过,CTE解决方案可以重构为联接的子查询(类似于问题中的临时表)。 在postgres中,如今,联接子查询通常比EXISTS()变体快。
DELETE FROM customer del
USING ( SELECT id
, row_number() over(partition by uuid order by created_date desc)
as rn
FROM customer
) sub
WHERE sub.id = del.id
AND sub.rn > 1
;
另一种方法是使用TEMP VIEW
。 这在语法上等效于temp table
情况,但在语义上等效于联接的子查询形式(至少在这种情况下,它们产生完全相同的查询计划)。 这是因为Postgres的优化程序会分解视图,并将其与主查询结合起来(上拉 )。 您可能会在PG中将view
视为一种宏。
CREATE TEMP VIEW targets
AS SELECT id
, row_number() over(partition by uuid ORDER BY created_date DESC) AS rn
FROM customer;
EXPLAIN
DELETE FROM customer
WHERE id IN ( SELECT id
FROM targets
WHERE rn > 1
);
[更新:我错了,因为CTE必须始终执行才能完成,只有数据修改CTE才是这种情况]
与使用临时表相比,使用CTE可能会导致不同的瓶颈。 我不熟悉PostgreSQL如何实现CTE,但是它很可能在内存中,因此,如果您的服务器内存不足,并且CTE的结果集很大,那么您可能会遇到问题。 我将在运行查询时监视服务器,并尝试查找瓶颈所在。
另一种执行删除的方法可能比两种方法都快:
DELETE C
FROM
Customer C
WHERE
EXISTS (SELECT * FROM Customer C2 WHERE C2.uuid = C.uuid AND C2.created_date > C.created_date)
那不会处理您与created_date
完全匹配的情况,但是也可以通过将id
添加到子查询中来解决。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.