Skip to content

41.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

因此,触发器和规则实现之间的速度不会有太大差异。

对于下一个删除,我们希望摆脱所有 2000 台hostnameold开头的计算机。有两种可能的命令可以做到这一点。一种是

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

在任何这些情况下,规则系统中的额外命令将或多或少独立于命令中受影响的行数。

总结是,如果规则的操作导致大而限定不佳的连接,即规划器失败的情况,则规则只会比触发器慢得多。