有两个查询树的细节我们在上面的视图规则中没有涉及到。 就是命令类型和结果关系。实际上,视图规则不需要这些信息。
一个SELECT的查询树和用于其他命令的查询树只有少数几个区别。 显然,它们不同的命令类型并且对于SELECT之外的命令, 结果关系指向结果将前往的范围表入口。任何其它东西都完全是一样的。 所以如果有两个表 t1 和 t2 分别有字段 a 和 b, 下面两个语句的查询树
SELECT t2.b FROM t1, t2 WHERE t1.a = t2.a;
UPDATE t1 SET b = t2.b WHERE t1.a = t2.a;
几乎是一样的。特别是:
范围表包含表 t1 和 t2 的记录。
目标列包含一个指向范围表 t2 表的字段 b 的变量。
条件表达式比较两个范围的字段 a 以寻找相等(行)。
连接树(jointree)显示 t1 和 t2 之间的简单连接。
结果是,两个查询树生成相似的执行规划:它们都是两个表的连接。 对于UPDATE语句来说, 规划器把 t1 缺失的字段追加到目标列因而最终查询树看起来象
UPDATE t1 SET a = t1.a, b = t2.b WHERE t1.a = t2.a;
因此执行器在连接上运行的结果和下面语句
SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a = t2.a;
是完全一样的。但是在UPDATE里有点问题: 执行器不关心它正在处理的从连接出来的结果的含义是什么。 它只是产生一个行的结果集。 一个是SELECT命令而另一个是UPDATE 命令的区别是由执行器的调用者控制的。 该调用者这时还知道(查看查询树)这是一个UPDATE, 而且它还知道结果要记录到表 t1 里去。 但是现有的记录中的哪一行要被新行取代呢?
要解决这个问题, 在UPDATE和DELETE语句的目标列表里面增加了另外一个入口。 当前的元组 ID(CTID)。 这是一个有着特殊特性的系统字段。 它包含行在(存储)块中的(存储)块数和位置信息。 在已知表的情况下,可以通过CTID 检索最初的需要更新的 t1 行。 在把CTID加到目标列表中去以后,查询看上去实际上象这样:
SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a;
现在,另一个 PostgreSQL 的细节进入到这个阶段里了。 这时,表中的旧行还没有被覆盖,这就是为什么 ROLLBACK 飞快的原因。 在一个UPDATE里,新的结果行插入到表里(在剥除CTID之后)并且把 CTID 指向的旧的数据行的元组头里面的 cmax和xmax 设置为当前命令计数器和当前事务 ID。这样旧的行就被隐藏起来并且在事务提交之后, vacuum 清理器就可以真正把它们删除掉。
知道了这些,我们就可以简单的把视图的规则应用到任意命令中。 它们(视图和命令)没有区别。