2010-04-21 181 views
33

我有大約500萬行,其具有FK約束引用另一個表(也約爲500萬行)的主鍵的表。的PostgreSQL - 禁用約束

我需要從兩個表中刪除大約75000行。我知道,如果我嘗試在啓用fk約束的情況下執行此操作,它將花費不可接受的時間。

從Oracle後臺我首先想到的是禁用約束的到來,做刪除&然後重新啓用約束。 PostGres似乎讓我禁用約束觸發器,如果​​我是超級用戶(我不是,但我作爲擁有/創建對象的用戶登錄),但似乎不是我想要的。

另一種選擇是刪除約束,然後恢復它。我擔心重建約束將會因爲我的表的大小而需要很長時間。

有什麼想法?

編輯:比利的鼓勵後,我試圖在不改變任何限制做了刪除,它需要超過10分鐘。但是,我發現我試圖刪除的表有一個自引用外鍵...重複(&未索引)。

最後更新 - 我放棄了自我參照外鍵,沒有我的刪除和添加它放回比利對周圍但不幸的是我不能接受的答案他的評論!

+4

如果它採取的是長,即使有500萬行,那麼你有什麼錯誤的設置。 – 2010-04-21 02:07:02

+0

什麼?刪除或重新啓用約束?是的,這很有可能是錯誤地或以不優化的方式設置的 - 數據庫幾乎已經被hibernate「構建」了(我沒有任何關係)。 – azp74 2010-04-21 02:10:07

+10

刪除。從索引表檢查FK需要線性時間,並刪除75000 + 75000行= 150 000行。考慮每個FK檢查(二進制搜索,lg(500萬)= 19)的最差情況19次比較,以及每行比較可能20次機器比較,等於57 000 000次比較。考慮到平均機器每秒可以做十億次比較的保守估計,很容易,這仍然需要不到一秒鐘的CPU時間。從磁盤加載也不應該是一個主要問題,因爲即使在500萬行中,表格也應該放在RAM中。 – 2010-04-21 02:15:43

回答

42

根據以前的評論,這應該是一個問題。也就是說,有一個命令可能就是你想要的 - 它將約束設置爲延遲,以便在COMMIT上進行檢查,而不是在每次刪除時進行檢查。如果你只是對所有行進行一次大的刪除操作,它將不會有所作爲,但是如果你正在分割它,它會。

SET CONSTRAINTS ALL DEFERRED 

是您在尋找的情況。請注意,在延遲之前,必須將約束標記爲DEFERRABLE。例如:

ALTER TABLE table_name 
    ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2) 
    DEFERRABLE INITIALLY IMMEDIATE; 

約束然後可以在一個事務或功能來推遲如下:

CREATE OR REPLACE FUNCTION f() RETURNS void AS 
$BODY$ 
BEGIN 
    SET CONSTRAINTS ALL DEFERRED; 

    -- Code that temporarily violates the constraint... 
    -- UPDATE table_name ... 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 
+1

當然值得一試,但我不相信延遲約束更快。 AFAIK他們只是將驗證工作從DELETE時間轉移到COMMIT時間。 – intgr 2010-04-21 12:31:21

+1

我會給這個去,但刪除fk並恢復工作。像intgr一樣,我想知道它是否不會僅僅改變fk的檢查來提交時間,所以我一定會記住下次。 – azp74 2010-04-22 10:51:59

+1

我丟棄了一個數據庫,並在運行SET CONSTRAINTS ALL DEFERRED後重新導入了它。一旦導入完成,是否有辦法「重新啓用」這些約束?這是一個相當龐大的文件,因此重新排列表格創建將非常困難。我通過兩次導入數據解決了這個問題。 – taco 2012-10-17 23:06:37

-7

禁用所有表約束

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName 

- 啓用所有表約束

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName 
+3

問題是關於Postgresql沒有那種能力(截至v9.4)。 – 2015-04-16 14:57:52

+0

同意v9.4沒有此功能 錯誤:在「NOCHECK」處或附近出現語法錯誤 第1行:ALTER TABLE TableName NOCHECK CONSTRAINT約束名稱 – 2015-08-05 11:50:39

3

(這個回答假設你的意圖是要刪除所有這些表的行的,不只是一個選擇。)

我也不得不這樣做,但作爲一個測試套件的一部分。我找到了答案,建議elsewhere on SO。使用TRUNCATE TABLE如下:

TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE]; 

下迅速刪除所有行從表table1table2table3,前提是有沒有列出從表這些表中的行沒有引用:

TRUNCATE TABLE table1, table2, table3; 

只要列出的表格之間有引用,PostgreSQL將刪除所有行,而不用擔心引用完整性。如果除列出的表以外的表引用這些表之一的行,則查詢將失敗。

但是,你可以有資格查詢,以便它也截斷與所列出的表引用的所有表(雖然我沒有試過):

TRUNCATE TABLE table1, table2, table3 CASCADE; 

默認情況下,這些表的序列不重新開始編號。新行將繼續下一個序列號。要重新啓動順序編號:

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY; 
7

什麼工作對我來說是一個地TRIGGERS那些會被捲入DELETE操作表來禁用一個。

ALTER TABLE reference DISABLE TRIGGER ALL; 
DELETE FROM reference WHERE refered_id > 1; 
ALTER TABLE reference ENABLE TRIGGER ALL; 

解決方案在9.3.16版本中工作。在我的情況下,時間從45分鐘到14秒,執行DELETE操作。

如@amphetamachine的評論部分所述,您需要具有admin特權才能執行此任務。

+1

請注意,執行「ALTER TABLE」命令的PostgreSQL用戶必須是該用戶的表。 – amphetamachine 2017-05-04 16:21:53

0

如果你試圖DISABLE TRIGGER ALL,並得到像permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger一個錯誤(我得到這個在亞馬遜RDS),試試這個:

set session_replication_role to replica; 

如果成功,背後表約束所有觸發器將被禁用。現在,您需要確保您的更改保持數據庫處於一致狀態!

然後,當你完成後,重新啓用觸發&約束與會話:

set session_replication_role to default;