PostgreSQL 规则系统介绍与使用
什么叫规则?
规则系统,更准确地说是查询重写规则系统,是 PostgreSQL 中一种在 SQL 命令执行前根据内部规则定义将其改写为另一个 SQL 命令的机制。
SELECT 规则
PostgreSQL 的视图是通过 SELECT 规则实现的。视图本质上是一个 SELECT 规则。
示例
sql
-- 创建表
CREATE TABLE t (id INT, name VARCHAR);
-- 创建视图
CREATE VIEW myview AS SELECT * FROM t;
等价于:
sql
CREATE TABLE myview (same column list as t);
CREATE RULE "_RETURN" AS
ON SELECT TO myview
DO INSTEAD SELECT * FROM t;
使用 \dt 和 \dv 查看表和视图,会发现视图实际上是一张表,并在其上附加了一个 SELECT 规则。
SELECT 规则的限制
- 后续动作只能是
INSTEAD。 - 规则的名称只能是
"_RETURN"。 - 只能创建在空表上。
错误示例
sql
CREATE TABLE mytab1(id INT, note VARCHAR(40));
CREATE TABLE mytab2(id INT, note VARCHAR(40));
-- 错误1:SELECT 规则必须使用 INSTEAD
CREATE RULE myrule AS ON SELECT TO mytab2 DO ALSO SELECT * FROM mytab1;
-- 错误2:规则名称必须为 "_RETURN"
CREATE RULE myrule AS ON SELECT TO mytab2 DO INSTEAD SELECT * FROM mytab1;
-- 正确:将 mytab2 变为一个视图
CREATE RULE "_RETURN" AS ON SELECT TO mytab2 DO INSTEAD SELECT * FROM mytab1;
更新规则
更新规则指的是针对 INSERT、UPDATE、DELETE 这 3 种 DML 操作的规则。
动作类型:ALSO 与 INSTEAD
- ALSO:执行原操作后,额外执行规则中的动作。
- INSTEAD:不执行原操作,只执行规则中的动作。
示例1:创建 INSERT 规则禁止插入数据
sql
CREATE TABLE t (id INT, name VARCHAR);
INSERT INTO t VALUES (1, 'aaa'), (2, 'aaadd');
-- 创建规则:执行 INSERT 时什么都不做
CREATE RULE t_r AS ON INSERT TO t DO INSTEAD NOTHING;
-- 尝试插入,不会插入数据
INSERT INTO t VALUES (7, 'cha');
示例2:记录表的所有 DML 操作
sql
-- 创建主表
CREATE TABLE mytab (
id INT PRIMARY KEY,
note TEXT
);
-- 创建操作记录表
CREATE TABLE mytab_log (
seq BIGSERIAL PRIMARY KEY,
oprtype CHAR(1),
oprtime TIMESTAMP,
old_id INT,
new_id INT,
old_note TEXT,
new_note TEXT
);
-- 创建 INSERT 规则
CREATE RULE rule_mytab_insert AS
ON INSERT TO mytab
DO ALSO
INSERT INTO mytab_log (oprtype, oprtime, new_id, new_note)
VALUES ('i', NOW(), NEW.id, NEW.note);
-- 创建 UPDATE 规则
CREATE RULE rule_mytab_update AS
ON UPDATE TO mytab
DO ALSO
INSERT INTO mytab_log (oprtype, oprtime, old_id, new_id, old_note, new_note)
VALUES ('u', NOW(), OLD.id, NEW.id, OLD.note, NEW.note);
-- 创建 DELETE 规则
CREATE RULE rule_mytab_delete AS
ON DELETE TO mytab
DO ALSO
INSERT INTO mytab_log (oprtype, oprtime, old_id, old_note)
VALUES ('d', NOW(), OLD.id, OLD.note);
对原表操作
sql
INSERT INTO mytab VALUES (1, 'note1');
UPDATE mytab SET note = 'updated' WHERE id = 1;
DELETE FROM mytab WHERE id = 1;
每条操作都会在 mytab_log 中留下记录。
规则与权限
规则从属于表或视图。如果一张表属于某个用户,则该表上的所有规则都属于该用户。用户只需要对查询中明确指定的表拥有所需权限即可进行操作。
示例
sql
-- 用户 osdba 创建视图
CREATE VIEW myview AS SELECT id FROM mytab;
GRANT SELECT ON myview TO user01;
如果用户 user01 对底层表 mytab 没有权限,但拥有视图 myview 的查询权限,则仍可通过视图访问数据。
修改规则
sql
ALTER RULE name ON table_name RENAME TO new_name;
name:现有规则的名称。table_name:规则所属的表或视图(可模式限定)。new_name:规则的新名称。
删除规则
sql
DROP RULE t_rule ON t;
规则与触发器的比较
| 特性 | 规则 | 触发器 |
|---|---|---|
| 实现方式 | 查询重写 | 逐行触发 |
| 适用场景 | 批量操作、查询改写 | 行级操作、复杂逻辑 |
| 概念复杂度 | 较高 | 较低 |
| 性能影响 | 可能生成更优计划 | 逐行处理可能较慢 |
何时使用规则?何时使用触发器?
- 规则:适用于批量操作或需要查询重写的场景,可能生成更好的执行计划,效率更高。
- 触发器:适用于逐行处理的复杂逻辑,概念简单,易于理解和维护。
总结
| 功能 | 说明 |
|---|---|
| SELECT 规则 | 用于实现视图,必须命名为 _RETURN,只能使用 INSTEAD |
| 更新规则 | 支持 INSERT/UPDATE/DELETE,可使用 ALSO 或 INSTEAD |
| 权限控制 | 规则从属于表/视图的所有者,用户只需显式权限 |
| 与触发器对比 | 规则更适合批量操作与查询重写,触发器更适合行级逻辑 |