[英]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.