支援的版本:目前 (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

這表明,當有多個資格條件表達式與 AND 結合時,規劃器沒有意識到 computerhostname 的資格條件也可以用於 software 上的索引掃描,這就是它在指令的規則表達式版本中所做的。對於必須刪除的 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

在任何這些情況下,來自規則系統的額外指令或多或少與指令中受影響的列數無關。

總結來說,只有在規則的動作導致大型且條件不佳的聯結(joins)時,規則才會明顯比觸發程序慢,也就是在規劃器(planner)失效的情況下。

提交更正

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