On this page
40.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
因此,触发器和规则实现之间的速度差异不会太大。
对于下一个删除,我们要删除hostname
以old
开头的所有 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
在这些情况下,来自规则系统的额外命令将或多或少地独立于命令中受影响的行数。
总结是,如果规则的动作导致大量的和不合格的联接,则规则将仅比触发器慢得多,这是计划者失败的情况。