利用触发器能做的大部分事情也可以利用 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
因此触发器和规则实现之间的速度差异不会太大。
在下一个删除操作中,我们希望摆脱所有 2000 台名称以 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
这表明规划器没有意识到,当存在多个与 AND
相结合的限定表达式时(这是在该命令的正则表达式版本中所做的事情),也可以将针对 computer
中的 hostname
的限定用于针对 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
在上述任何情况下,规则系统中的额外命令将或多或少独立于命令中受影响的行数。
总结是,规则仅在它们的执行导致大型且限定不当的联接(规划器无法处理的情况)时才会显着低于触发器。