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

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

对于下一个删除,我们要删除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结合使用时,computer中的hostname的限定符也可以用于software上的索引扫描,这在命令的正则表达式版本中是一样的。对于必须删除的 2000 台旧计算机中的每台,触发器将被调用一次,这将导致对computer进行一次索引扫描,对software进行 2000 次索引扫描。规则实现将使用两个使用索引的命令来实现。并且,依序表software的整体大小在连续扫描情况下该规则是否还会更快。即使所有索引块很快就会在高速缓存中,通过 SPIManagement 器从触发器执行 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

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

总结是,如果规则的动作导致大量的和不合格的联接,则规则将仅比触发器慢得多,这是计划者失败的情况。