Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

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

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

在下一个删除操作中,我们希望摆脱所有 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

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

总结是,规则仅在它们的执行导致大型且限定不当的联接(规划器无法处理的情况)时才会显着低于触发器。