簡體   English   中英

在Postgres中使用CTE進行刪除比使用temp表慢

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM