2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

39.7. 規則與觸發器 #

許多使用觸發器可以完成的事情也可以透過 PostgreSQL 規則系統來實現。規則無法實現的一些約束,特別是外部索引鍵,是規則無法做到的。可以放置一個限定規則,如果某一列的值不存在於另一張表中,則將命令重寫為 NOTHING。但那樣資料會被靜默丟棄,這不是個好主意。如果需要對有效值進行檢查,並在值為無效時生成錯誤訊息,則必須透過觸發器完成。

在本章中,我們專注於使用規則來更新檢視。本章中所有更新規則的示例也可以使用檢視上的 INSTEAD OF 觸發器來實現。編寫這類觸發器通常比編寫規則更容易,尤其是當需要複雜的邏輯來執行更新時。

對於兩者都可以實現的功能,哪種更好取決於資料庫的使用方式。觸發器對每個受影響的行都會觸發一次。規則修改查詢或生成額外的查詢。因此,如果一個語句影響了許多行,那麼一個發出一個額外命令的規則可能比一個為每一行呼叫並必須多次重新確定做什麼的觸發器更快。然而,觸發器方法在概念上比規則方法簡單得多,而且新手更容易正確使用。

這裡我們展示了一個關於規則與觸發器選擇在一種情況下如何發揮作用的例子。有兩張表

CREATE TABLE computer (
    hostname        text,    -- indexed
    manufacturer    text     -- indexed
);

CREATE TABLE software (
    software        text,    -- indexed
    hostname        text     -- indexed
);

兩張表都有數萬行,並且 hostname 上的索引是唯一的。規則或觸發器應實現一個約束,刪除 software 中引用了已刪除計算機的行。觸發器將使用此命令

DELETE FROM software WHERE hostname = $1;

由於觸發器是為從 computer 中刪除的每一行單獨呼叫的,因此它可以準備並儲存該命令的計劃,並將 hostname 值作為引數傳遞。規則的寫法如下

CREATE RULE computer_del AS ON DELETE TO computer
    DO DELETE FROM software WHERE hostname = OLD.hostname;

現在我們來看不同型別的刪除。在以下情況下

DELETE FROM computer WHERE hostname = 'mypc.local.net';

computer 透過索引掃描(速度快),觸發器發出的命令也將使用索引掃描(速度也快)。規則產生的額外命令將是

DELETE FROM software WHERE computer.hostname = 'mypc.local.net'
                       AND software.hostname = computer.hostname;

由於設定了適當的索引,規劃器將建立一個計劃

Nestloop
  ->  Index Scan using comp_hostidx on computer
  ->  Index Scan using soft_hostidx on software

因此,觸發器和規則實現之間的速度差異不會太大。

對於下一個刪除,我們想刪除所有 hostnameold 開頭的 2000 臺計算機。有兩種可能的命令可以做到這一點。一種是

DELETE FROM computer WHERE hostname >= 'old'
                       AND hostname <  'ole'

規則新增的命令將是

DELETE FROM software WHERE computer.hostname >= 'old' AND computer.hostname < 'ole'
                       AND software.hostname = computer.hostname;

計劃是

Hash Join
  ->  Seq Scan on software
  ->  Hash
    ->  Index Scan using comp_hostidx on computer

另一種可能的命令是

DELETE FROM computer WHERE hostname ~ '^old';

這導致了規則新增的命令的以下執行計劃

Nestloop
  ->  Index Scan using comp_hostidx on computer
  ->  Index Scan using soft_hostidx on software

這表明,規劃器沒有意識到 computerhostname 的限定條件也可以用於 software 上的索引掃描,當有多個限定條件透過 AND 組合在一起時,這正是它在命令的正則表示式版本中所做的。觸發器將針對需要刪除的 2000 臺舊計算機中的每一臺呼叫一次,這將導致對 computer 的一次索引掃描和對 software 的 2000 次索引掃描。規則實現將透過兩個使用索引的命令來完成。而且,這取決於 software 表的總體大小,規則在順序掃描情況下是否仍然更快。觸發器透過 SPI 管理器執行 2000 條命令需要一些時間,即使所有索引塊都會很快進入快取。

我們看的最後一個命令是

DELETE FROM computer WHERE manufacturer = 'bim';

這同樣可能導致從 computer 中刪除許多行。因此,觸發器將再次透過執行器執行許多命令。規則生成的命令將是

DELETE FROM software WHERE computer.manufacturer = 'bim'
                       AND software.hostname = computer.hostname;

該命令的計劃將再次是兩個索引掃描的巢狀迴圈,只是使用了 computer 上的一個不同的索引

Nestloop
  ->  Index Scan using comp_manufidx on computer
  ->  Index Scan using soft_hostidx on software

在任何這些情況下,規則系統的額外命令或多或少都獨立於命令中受影響的行數。

總結來說,只有當規則的操作導致了大型且糟糕的連線(一種規劃器失敗的情況)時,規則才會比觸發器明顯慢。

提交更正

如果您在文件中發現任何不正確、不符合您對特定功能的體驗或需要進一步澄清的內容,請使用 此表格 報告文件問題。