支持的版本:当前 (17) / 16 / 15 / 14 / 13
开发版本:开发版
不支持的版本: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

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

总而言之,如果规则的操作导致大型且限定不良的连接(规划器失败的情况),规则才会明显比触发器慢。

提交更正

如果您发现文档中的任何内容不正确、与您使用特定功能的体验不符或需要进一步澄清,请使用此表单报告文档问题。