【IT168 伺服器學院】 許多用觸發器可以幹的事情同樣也可以用 PostgreSQL 規則系統來完成。 目前不能用規則來實現的東西之一是某些約束,特別是外鍵。 我們可能在某欄位的值沒有在另一個表裡出現的情況下用一條有條件的規則把查詢重寫為 NOTHING。 不過這樣做數據就會被不聲不響的被仍掉,因而這也不是一個好主意。 如果需要檢查有效的值,而且如果是無效值出現時要生成一個錯誤信息, 這種情況下我們要用觸發器來做。
另一方面,一個用於INSERT一個視圖的觸發器可以做到與規則一樣, 把數據放到另外的地方去而取代對視圖的插入。 但它不能在UPDATE或DELETE時做同樣的事情, 因為在視圖關係裡沒有可供掃描的真實數據,因而觸發器將永遠不被調用。 這時只有規則可用。
對於兩者都可用的情況,哪個更好取決於對資料庫的使用。 觸發器為任何涉及到的行執行一次。規則修改查詢樹或生成額外的查詢。 所以如果在一個語句中涉及到多行, 一個生成一個額外查詢的規則通常可能會比一個對每一行都分別執行一次(因此要執行很多次)的觸發器要快一些。 不過,觸發器的方法從概念上要遠比規則的方法簡單,並且很容易讓新手可以做正確事情。
例如:這裡有兩個表
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
所以在規則和觸發器的實現之間沒有太多的速度差別。
下面的刪除我們希望刪掉所有 2000 個 hostname 以 old 開頭的計算機。 有兩個可能的用於這個用途的查詢。一個是
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
這表明,規劃器不能認識到表 computer 裡的hostname (計算機主機名)的條件在多個條件表達式以 AND 的方式組合在一起時同樣可以用於 software,就象在用正則表達式的查詢裡一樣。 觸發器將在任何 2000 個要被刪除的舊計算機裡被調用一次, 結果是對 computer 的一次索引掃描和對 software 的2000次索引掃描。 規則的實現將在兩個對索引的查詢實現之。 所以這是由 software 表的實際大小決定規則進行了順序掃描後是否還是快一些。 2000 個在 SPI 管理器上的查詢的執行是要點時間的, 即使所有要使用的索引塊都很快在緩衝裡出現。
我們看的最後一個查詢是
DELETE FROM computer WHERE manufacurer = ''bim'';
同樣,這也會導致從 computer 表裡的多行刪除。 所以觸發器同樣會向執行器提交很多查詢。規則生成的命令將會是
DELETE FROM software WHERE computer.manufacurer = ''bim''
AND software.hostname = computer.hostname;
但規則規劃又將是對兩個索引掃描的嵌套循環。 只是用了 computer 的另外一個索引:
Nestloop
-> Index Scan using comp_manufidx on computer
-> Index Scan using soft_hostidx on software
在任何一種情況下,從規則系統出來的額外查詢都或多或少與查詢中涉及到的行的數量相對獨立。
概括來說,規則只是在它們的動作(action)生成了又大又爛的條件連接時才比觸發器有較大速度差異, 這時規劃器將失效