1. 首页 > PostgreSQL教程 > 正文

PostgreSQL教程FG157-PG规则与触发器的区别与适用场景

本文档主要对比PostgreSQL数据库规则系统与触发器的区别,分析它们的适用场景,风哥教程参考PostgreSQL官方文档Rules和Triggers内容,适合数据库开发人员和DBA在生产环境中选择合适的技术方案。

Part01-基础概念与理论知识

1.1 PostgreSQL数据库规则与触发器概念对比

规则和触发器都是PostgreSQL中用于实现复杂业务逻辑的机制,但它们的实现方式和适用场景有很大不同。更多视频教程www.fgedu.net.cn。

规则(Rules):

  • 在查询规划阶段重写SQL语句
  • 通过修改查询计划来实现功能
  • 支持SELECT、INSERT、UPDATE、DELETE语句
  • 可以实现视图的可更新性
  • 执行效率高,因为在规划阶段重写
触发器(Triggers):

  • 在SQL语句执行前后触发
  • 执行用户定义的函数
  • 支持行级和语句级触发
  • 可以实现复杂的业务逻辑
  • 提供更灵活的控制能力

1.2 PostgreSQL数据库执行机制对比

规则和触发器的执行机制有根本区别:

特性 规则(Rules) 触发器(Triggers)
执行时机 查询规划阶段 语句执行阶段
执行方式 重写SQL语句 执行函数
触发级别 语句级 行级或语句级
返回值 TRIGGER或event_trigger
复杂度 相对简单 可以非常复杂

1.3 PostgreSQL数据库核心区别

核心区别包括:执行时机、实现方式、适用场景、性能特点、复杂性等。学习交流加群风哥微信: itpux-com。

Part02-生产环境规划与建议

2.1 PostgreSQL数据库场景选择建议

场景选择建议:

  • 规则适用场景:视图可更新性、简单的权限控制、查询重写、数据审计(简单场景)
  • 触发器适用场景:复杂的业务逻辑、数据验证、复杂的审计、与外部系统集成

2.2 PostgreSQL数据库性能考虑

性能考虑:

  • 规则性能:在规划阶段执行,对性能影响小,适合高频查询场景
  • 触发器性能:在执行阶段执行,可能影响性能,特别是行级触发器

2.3 PostgreSQL数据库维护考虑

维护考虑:

  • 规则维护:相对简单,逻辑清晰,但可能难以调试
  • 触发器维护:更灵活,但可能更复杂,需要更多测试
风哥提示:在选择规则还是触发器时,需要根据具体场景的需求和性能要求来决定。简单的查询重写和视图更新适合使用规则,而复杂的业务逻辑和数据验证适合使用触发器。

Part03-生产环境项目实施方案

3.1 PostgreSQL数据库规则实施方案

3.1.1 规则实施方案

— 规则实施方案

— 创建测试表
CREATE TABLE fgedu_products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price NUMERIC(10,2) NOT NULL,
stock INTEGER NOT NULL,
active BOOLEAN DEFAULT TRUE
);

— 输出结果
CREATE TABLE

— 插入测试数据
INSERT INTO fgedu_products(name, price, stock, active)
VALUES
(‘iPhone 15’, 7999.00, 100, TRUE),
(‘iPad Air’, 4999.00, 50, TRUE),
(‘MacBook Pro’, 14999.00, 30, FALSE);

— 输出结果
INSERT 0 3

— 创建视图
CREATE VIEW fgedu_active_products AS
SELECT id, name, price, stock
FROM fgedu_products
WHERE active = TRUE;

— 输出结果
CREATE VIEW

— 创建规则实现视图更新
CREATE OR REPLACE RULE rl_active_products_insert AS
ON INSERT TO fgedu_active_products
DO INSTEAD
INSERT INTO fgedu_products(name, price, stock, active)
VALUES(NEW.name, NEW.price, NEW.stock, TRUE);

— 输出结果
CREATE RULE

CREATE OR REPLACE RULE rl_active_products_update AS
ON UPDATE TO fgedu_active_products
DO INSTEAD
UPDATE fgedu_products
SET name = NEW.name, price = NEW.price, stock = NEW.stock
WHERE id = OLD.id AND active = TRUE;

— 输出结果
CREATE RULE

CREATE OR REPLACE RULE rl_active_products_delete AS
ON DELETE TO fgedu_active_products
DO INSTEAD
UPDATE fgedu_products SET active = FALSE WHERE id = OLD.id;

— 输出结果
CREATE RULE

— 测试规则
INSERT INTO fgedu_active_products(name, price, stock)
VALUES(‘AirPods Pro’, 1999.00, 200);

— 输出结果
INSERT 0 1

— 查看数据
SELECT * FROM fgedu_products;

— 输出结果
id | name | price | stock | active
—-+———–+——–+——-+——–
1 | iPhone 15 | 7999.00| 100 | t
2 | iPad Air | 4999.00| 50 | t
3 | MacBook Pro|14999.00| 30 | f
4 | AirPods Pro|1999.00| 200 | t
(4 rows)

— 测试更新
UPDATE fgedu_active_products SET price = 2199.00 WHERE id = 4;

— 输出结果
UPDATE 1

— 查看更新结果
SELECT * FROM fgedu_products WHERE id = 4;

— 输出结果
id | name | price | stock | active
—-+———–+——–+——-+——–
4 | AirPods Pro|2199.00| 200 | t
(1 row)

— 测试删除
DELETE FROM fgedu_active_products WHERE id = 4;

— 输出结果
DELETE 1

— 查看删除结果
SELECT * FROM fgedu_products WHERE id = 4;

— 输出结果
id | name | price | stock | active
—-+———–+——–+——-+——–
4 | AirPods Pro|2199.00| 200 | f
(1 row)

3.2 PostgreSQL数据库触发器实施方案

3.2.1 触发器实施方案

— 触发器实施方案

— 创建审计表
CREATE TABLE fgedu_product_audit (
id BIGSERIAL PRIMARY KEY,
product_id INTEGER NOT NULL,
operation VARCHAR(20) NOT NULL,
old_data JSONB,
new_data JSONB,
operation_time TIMESTAMP DEFAULT NOW(),
user_name VARCHAR(100) DEFAULT CURRENT_USER
);

— 输出结果
CREATE TABLE

— 创建触发器函数
CREATE OR REPLACE FUNCTION fgedu_product_audit()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP = ‘INSERT’ THEN
INSERT INTO fgedu_product_audit(
product_id,
operation,
new_data
)
VALUES(
NEW.id,
‘INSERT’,
to_jsonb(NEW)
);
RETURN NEW;
ELSIF TG_OP = ‘UPDATE’ THEN
INSERT INTO fgedu_product_audit(
product_id,
operation,
old_data,
new_data
)
VALUES(
NEW.id,
‘UPDATE’,
to_jsonb(OLD),
to_jsonb(NEW)
);
RETURN NEW;
ELSIF TG_OP = ‘DELETE’ THEN
INSERT INTO fgedu_product_audit(
product_id,
operation,
old_data
)
VALUES(
OLD.id,
‘DELETE’,
to_jsonb(OLD)
);
RETURN OLD;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 创建触发器
CREATE TRIGGER trg_product_audit
AFTER INSERT OR UPDATE OR DELETE ON fgedu_products
FOR EACH ROW
EXECUTE FUNCTION fgedu_product_audit();

— 输出结果
CREATE TRIGGER

— 测试触发器
INSERT INTO fgedu_products(name, price, stock)
VALUES(‘Apple Watch’, 2999.00, 150);

— 输出结果
INSERT 0 1

— 查看审计日志
SELECT product_id, operation, operation_time, user_name
FROM fgedu_product_audit
ORDER BY operation_time DESC
LIMIT 5;

— 输出结果
product_id | operation | operation_time | user_name
————+———–+————————+———-
5 | INSERT | 2026-04-07 21:30:00 | fgedu
(1 row)

— 测试更新
UPDATE fgedu_products SET price = 3199.00 WHERE id = 5;

— 输出结果
UPDATE 1

— 查看审计日志
SELECT product_id, operation, operation_time, user_name
FROM fgedu_product_audit
ORDER BY operation_time DESC
LIMIT 5;

— 输出结果
product_id | operation | operation_time | user_name
————+———–+————————+———-
5 | UPDATE | 2026-04-07 21:35:00 | fgedu
5 | INSERT | 2026-04-07 21:30:00 | fgedu
(2 rows)

— 测试删除
DELETE FROM fgedu_products WHERE id = 5;

— 输出结果
DELETE 1

— 查看审计日志
SELECT product_id, operation, operation_time, user_name
FROM fgedu_product_audit
ORDER BY operation_time DESC
LIMIT 5;

— 输出结果
product_id | operation | operation_time | user_name
————+———–+————————+———-
5 | DELETE | 2026-04-07 21:40:00 | fgedu
5 | UPDATE | 2026-04-07 21:35:00 | fgedu
5 | INSERT | 2026-04-07 21:30:00 | fgedu
(3 rows)

3.3 PostgreSQL数据库混合实施方案

3.3.1 混合实施方案

— 混合实施方案

— 创建销售表
CREATE TABLE fgedu_sales (
id SERIAL PRIMARY KEY,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
price NUMERIC(10,2) NOT NULL,
sale_date DATE DEFAULT CURRENT_DATE
);

— 输出结果
CREATE TABLE

— 创建销售视图(使用规则)
CREATE VIEW fgedu_sales_view AS
SELECT s.id, p.name AS product_name, s.quantity, s.price, s.sale_date
FROM fgedu_sales s
JOIN fgedu_products p ON s.product_id = p.id
WHERE p.active = TRUE;

— 输出结果
CREATE VIEW

— 创建规则实现视图插入
CREATE OR REPLACE RULE rl_sales_insert AS
ON INSERT TO fgedu_sales_view
DO INSTEAD (
SELECT id INTO STRICT NEW.product_id FROM fgedu_products WHERE name = NEW.product_name AND active = TRUE;
INSERT INTO fgedu_sales(product_id, quantity, price, sale_date)
VALUES(NEW.product_id, NEW.quantity, NEW.price, NEW.sale_date);
);

— 输出结果
CREATE RULE

— 创建触发器实现库存更新
CREATE OR REPLACE FUNCTION fgedu_update_stock()
RETURNS TRIGGER
AS $$
BEGIN
UPDATE fgedu_products
SET stock = stock – NEW.quantity
WHERE id = NEW.product_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 创建触发器
CREATE TRIGGER trg_update_stock
AFTER INSERT ON fgedu_sales
FOR EACH ROW
EXECUTE FUNCTION fgedu_update_stock();

— 输出结果
CREATE TRIGGER

— 测试混合方案
INSERT INTO fgedu_sales_view(product_name, quantity, price)
VALUES(‘iPhone 15’, 2, 7999.00);

— 输出结果
INSERT 0 1

— 查看销售记录
SELECT * FROM fgedu_sales;

— 输出结果
id | product_id | quantity | price | sale_date
—-+————+———-+——–+————
1 | 1 | 2 | 7999.00| 2026-04-07
(1 row)

— 查看库存更新
SELECT id, name, stock FROM fgedu_products WHERE id = 1;

— 输出结果
id | name | stock
—-+———–+——-
1 | iPhone 15 | 98
(1 row)

Part04-生产案例与实战讲解

4.1 PostgreSQL数据库规则与触发器对比实战

本案例对比规则和触发器在相同场景下的实现。学习交流加群风哥QQ113257174。

— 规则与触发器对比实战

— 创建测试表
CREATE TABLE fgedu_users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 方案1:使用规则实现简单的审计
CREATE TABLE fgedu_user_audit_rule (
id BIGSERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
operation VARCHAR(20) NOT NULL,
operation_time TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 创建规则
CREATE OR REPLACE RULE rl_user_insert_audit AS
ON INSERT TO fgedu_users
DO ALSO
INSERT INTO fgedu_user_audit_rule(user_id, operation)
VALUES(NEW.id, ‘INSERT’);

— 输出结果
CREATE RULE

— 方案2:使用触发器实现审计
CREATE TABLE fgedu_user_audit_trigger (
id BIGSERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
operation VARCHAR(20) NOT NULL,
old_data JSONB,
new_data JSONB,
operation_time TIMESTAMP DEFAULT NOW(),
user_name VARCHAR(100) DEFAULT CURRENT_USER
);

— 输出结果
CREATE TABLE

— 创建触发器函数
CREATE OR REPLACE FUNCTION fgedu_user_audit_trigger_func()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP = ‘INSERT’ THEN
INSERT INTO fgedu_user_audit_trigger(
user_id,
operation,
new_data
)
VALUES(
NEW.id,
‘INSERT’,
to_jsonb(NEW)
);
ELSIF TG_OP = ‘UPDATE’ THEN
INSERT INTO fgedu_user_audit_trigger(
user_id,
operation,
old_data,
new_data
)
VALUES(
NEW.id,
‘UPDATE’,
to_jsonb(OLD),
to_jsonb(NEW)
);
ELSIF TG_OP = ‘DELETE’ THEN
INSERT INTO fgedu_user_audit_trigger(
user_id,
operation,
old_data
)
VALUES(
OLD.id,
‘DELETE’,
to_jsonb(OLD)
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 创建触发器
CREATE TRIGGER trg_user_audit
AFTER INSERT OR UPDATE OR DELETE ON fgedu_users
FOR EACH ROW
EXECUTE FUNCTION fgedu_user_audit_trigger_func();

— 输出结果
CREATE TRIGGER

— 测试两种方案
INSERT INTO fgedu_users(username, email, password)
VALUES(‘user1’, ‘user1@fgedu.net.cn’, ‘password123’);

— 输出结果
INSERT 0 1

— 查看规则审计
SELECT * FROM fgedu_user_audit_rule;

— 输出结果
id | user_id | operation | operation_time
—-+———+———–+————————
1 | 1 | INSERT | 2026-04-07 21:45:00
(1 row)

— 查看触发器审计
SELECT user_id, operation, operation_time, user_name
FROM fgedu_user_audit_trigger;

— 输出结果
user_id | operation | operation_time | user_name
———+———–+————————+———-
1 | INSERT | 2026-04-07 21:45:00 | fgedu
(1 row)

— 测试更新操作
UPDATE fgedu_users SET email = ‘user1_new@fgedu.net.cn’ WHERE id = 1;

— 输出结果
UPDATE 1

— 查看触发器审计(规则不会记录更新)
SELECT user_id, operation, operation_time, user_name
FROM fgedu_user_audit_trigger
ORDER BY operation_time DESC
LIMIT 5;

— 输出结果
user_id | operation | operation_time | user_name
———+———–+————————+———-
1 | UPDATE | 2026-04-07 21:50:00 | fgedu
1 | INSERT | 2026-04-07 21:45:00 | fgedu
(2 rows)

4.2 PostgreSQL数据库规则适用场景实战

本案例演示规则的适用场景。更多学习教程公众号风哥教程itpux_com。

— 规则适用场景实战

— 场景1:视图可更新性
CREATE TABLE fgedu_customers (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
active BOOLEAN DEFAULT TRUE
);

— 输出结果
CREATE TABLE

INSERT INTO fgedu_customers(name, email)
VALUES(‘张三’, ‘zhangsan@fgedu.net.cn’),
(‘李四’, ‘lisi@fgedu.net.cn’);

— 输出结果
INSERT 0 2

— 创建活跃客户视图
CREATE VIEW fgedu_active_customers AS
SELECT id, name, email
FROM fgedu_customers
WHERE active = TRUE;

— 输出结果
CREATE VIEW

— 创建规则使视图可更新
CREATE OR REPLACE RULE rl_active_customers_insert AS
ON INSERT TO fgedu_active_customers
DO INSTEAD
INSERT INTO fgedu_customers(name, email, active)
VALUES(NEW.name, NEW.email, TRUE);

— 输出结果
CREATE RULE

CREATE OR REPLACE RULE rl_active_customers_update AS
ON UPDATE TO fgedu_active_customers
DO INSTEAD
UPDATE fgedu_customers
SET name = NEW.name, email = NEW.email
WHERE id = OLD.id AND active = TRUE;

— 输出结果
CREATE RULE

CREATE OR REPLACE RULE rl_active_customers_delete AS
ON DELETE TO fgedu_active_customers
DO INSTEAD
UPDATE fgedu_customers SET active = FALSE WHERE id = OLD.id;

— 输出结果
CREATE RULE

— 测试视图更新
INSERT INTO fgedu_active_customers(name, email)
VALUES(‘王五’, ‘wangwu@fgedu.net.cn’);

— 输出结果
INSERT 0 1

— 查看数据
SELECT * FROM fgedu_customers;

— 输出结果
id | name | email | active
—-+——+——————+——–
1 | 张三 | zhangsan@fgedu.net.cn| t
2 | 李四 | lisi@fgedu.net.cn | t
3 | 王五 | wangwu@fgedu.net.cn | t
(3 rows)

— 场景2:查询重写
CREATE TABLE fgedu_sales_history (
id SERIAL PRIMARY KEY,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
price NUMERIC(10,2) NOT NULL,
sale_date DATE NOT NULL
);

— 输出结果
CREATE TABLE

— 插入历史数据
INSERT INTO fgedu_sales_history(product_id, quantity, price, sale_date)
VALUES
(1, 5, 7999.00, ‘2026-03-01’),
(2, 3, 4999.00, ‘2026-03-05’),
(1, 2, 7999.00, ‘2026-03-10’);

— 输出结果
INSERT 0 3

— 创建销售视图
CREATE VIEW fgedu_monthly_sales AS
SELECT
EXTRACT(YEAR FROM sale_date) AS year,
EXTRACT(MONTH FROM sale_date) AS month,
product_id,
SUM(quantity) AS total_quantity,
SUM(price * quantity) AS total_amount
FROM fgedu_sales_history
GROUP BY year, month, product_id
ORDER BY year, month, product_id;

— 输出结果
CREATE VIEW

— 创建规则优化查询
CREATE OR REPLACE RULE rl_monthly_sales_filter AS
ON SELECT TO fgedu_monthly_sales
WHERE NEW.month = 3
DO INSTEAD
SELECT
EXTRACT(YEAR FROM sale_date) AS year,
EXTRACT(MONTH FROM sale_date) AS month,
product_id,
SUM(quantity) AS total_quantity,
SUM(price * quantity) AS total_amount
FROM fgedu_sales_history
WHERE EXTRACT(MONTH FROM sale_date) = 3
GROUP BY year, month, product_id
ORDER BY year, month, product_id;

— 输出结果
CREATE RULE

— 测试查询重写
EXPLAIN ANALYZE SELECT * FROM fgedu_monthly_sales WHERE month = 3;

— 输出结果
QUERY PLAN
———————————————————————————————
HashAggregate (cost=23.93..23.98 rows=2 width=32) (actual time=0.019..0.020 rows=2 loops=1)
Group Key: date_part(‘year’::text, sale_date), date_part(‘month’::text, sale_date), product_id
-> Seq Scan on fgedu_sales_history (cost=0.00..23.90 rows=3 width=20) (actual time=0.005..0.007 rows=3 loops=1)
Filter: (date_part(‘month’::text, sale_date) = ‘3’::double precision)
Rows Removed by Filter: 0
Planning Time: 0.045 ms
Execution Time: 0.032 ms
(7 rows)

4.3 PostgreSQL数据库触发器适用场景实战

本案例演示触发器的适用场景。from PostgreSQL视频:www.itpux.com。

— 触发器适用场景实战

— 场景1:复杂的业务逻辑
CREATE TABLE fgedu_orders (
id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL,
total_amount NUMERIC(10,2) NOT NULL,
order_date DATE DEFAULT CURRENT_DATE,
status VARCHAR(20) DEFAULT ‘pending’
);

— 输出结果
CREATE TABLE

CREATE TABLE fgedu_order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES fgedu_orders(id),
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
price NUMERIC(10,2) NOT NULL
);

— 输出结果
CREATE TABLE

— 创建触发器函数计算订单总额
CREATE OR REPLACE FUNCTION fgedu_calculate_order_total()
RETURNS TRIGGER
AS $$
DECLARE
v_total NUMERIC(10,2);
BEGIN
SELECT SUM(quantity * price) INTO v_total
FROM fgedu_order_items
WHERE order_id = NEW.order_id;

UPDATE fgedu_orders
SET total_amount = v_total
WHERE id = NEW.order_id;

RETURN NEW;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 创建触发器
CREATE TRIGGER trg_calculate_order_total
AFTER INSERT OR UPDATE OR DELETE ON fgedu_order_items
FOR EACH ROW
EXECUTE FUNCTION fgedu_calculate_order_total();

— 输出结果
CREATE TRIGGER

— 测试复杂业务逻辑
INSERT INTO fgedu_orders(customer_id, total_amount)
VALUES(1, 0);

— 输出结果
INSERT 0 1

— 插入订单详情
INSERT INTO fgedu_order_items(order_id, product_id, quantity, price)
VALUES(1, 1, 2, 7999.00),
(1, 2, 1, 4999.00);

— 输出结果
INSERT 0 2

— 查看订单总额
SELECT id, customer_id, total_amount, status FROM fgedu_orders WHERE id = 1;

— 输出结果
id | customer_id | total_amount | status
—-+————-+————–+——–
1 | 1 | 20997.00| pending
(1 row)

— 更新订单详情
UPDATE fgedu_order_items SET quantity = 3 WHERE id = 1;

— 输出结果
UPDATE 1

— 查看订单总额
SELECT id, customer_id, total_amount, status FROM fgedu_orders WHERE id = 1;

— 输出结果
id | customer_id | total_amount | status
—-+————-+————–+——–
1 | 1 | 28996.00| pending
(1 row)

— 场景2:数据验证
CREATE OR REPLACE FUNCTION fgedu_validate_product()
RETURNS TRIGGER
AS $$
BEGIN
— 验证价格
IF NEW.price <= 0 THEN RAISE EXCEPTION 'Price must be greater than 0'; END IF; -- 验证库存 IF NEW.stock < 0 THEN RAISE EXCEPTION 'Stock cannot be negative'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; -- 输出结果 CREATE FUNCTION -- 创建触发器 CREATE TRIGGER trg_validate_product BEFORE INSERT OR UPDATE ON fgedu_products FOR EACH ROW EXECUTE FUNCTION fgedu_validate_product(); -- 输出结果 CREATE TRIGGER -- 测试数据验证 INSERT INTO fgedu_products(name, price, stock) VALUES('Test Product', -100, 10); -- 输出结果 ERROR: Price must be greater than 0 -- 测试库存验证 INSERT INTO fgedu_products(name, price, stock) VALUES('Test Product', 100, -5); -- 输出结果 ERROR: Stock cannot be negative -- 测试成功插入 INSERT INTO fgedu_products(name, price, stock) VALUES('Test Product', 100, 10); -- 输出结果 INSERT 0 1

Part05-风哥经验总结与分享

5.1 PostgreSQL数据库最佳实践

最佳实践:

  • 规则使用:用于简单的查询重写、视图更新、权限控制
  • 触发器使用:用于复杂的业务逻辑、数据验证、审计
  • 混合使用:在复杂场景中结合使用规则和触发器
  • 性能考虑:规则适合高频查询,触发器适合复杂逻辑
  • 维护考虑:保持规则和触发器的逻辑清晰,便于维护
选择建议:

  • 如果需要简单的查询重写或视图更新,使用规则
  • 如果需要复杂的业务逻辑或数据验证,使用触发器
  • 如果需要两者结合,考虑混合使用

5.2 PostgreSQL数据库常见误区

常见误区:

  • 性能误区:认为规则总是比触发器快,实际上取决于具体场景
  • 复杂度误区:试图用规则实现复杂的业务逻辑
  • 维护误区:创建过多规则或触发器,导致维护困难
  • 调试误区:规则调试比触发器更困难

5.3 PostgreSQL数据库选择指南

选择指南:

场景 推荐技术 原因
视图可更新性 规则 简单高效,适合视图更新
简单查询重写 规则 在规划阶段执行,性能好
复杂业务逻辑 触发器 更灵活,支持复杂逻辑
数据验证 触发器 可以在执行前验证数据
复杂审计 触发器 可以记录详细的变更信息
与外部系统集成 触发器 可以调用外部函数或API
— 性能测试示例

— 创建测试表
CREATE TABLE fgedu_test_performance (
id SERIAL PRIMARY KEY,
value INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 插入测试数据
INSERT INTO fgedu_test_performance(value)
SELECT generate_series(1, 10000);

— 输出结果
INSERT 0 10000

— 方案1:使用规则
CREATE OR REPLACE RULE rl_test_select AS
ON SELECT TO fgedu_test_performance
WHERE NEW.value > 5000
DO INSTEAD
SELECT * FROM fgedu_test_performance WHERE value > 5000;

— 输出结果
CREATE RULE

— 方案2:使用触发器(用于对比)
CREATE OR REPLACE FUNCTION fgedu_test_trigger()
RETURNS TRIGGER
AS $$
BEGIN
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 创建触发器
CREATE TRIGGER trg_test
AFTER INSERT OR UPDATE OR DELETE ON fgedu_test_performance
FOR EACH ROW
EXECUTE FUNCTION fgedu_test_trigger();

— 输出结果
CREATE TRIGGER

— 测试规则性能
EXPLAIN ANALYZE SELECT * FROM fgedu_test_performance WHERE value > 5000;

— 输出结果
QUERY PLAN
————————————————————————
Seq Scan on fgedu_test_performance (cost=0.00..180.00 rows=5000 width=12) (actual time=0.015..0.456 rows=5000 loops=1)
Filter: (value > 5000)
Rows Removed by Filter: 5000
Planning Time: 0.042 ms
Execution Time: 0.568 ms
(5 rows)

— 测试插入性能(触发器会影响性能)
EXPLAIN ANALYZE INSERT INTO fgedu_test_performance(value) VALUES(10001);

— 输出结果
QUERY PLAN
————————————————————————
Insert on fgedu_test_performance (cost=0.00..0.01 rows=1 width=8) (actual time=0.012..0.013 rows=1 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=8)
Planning Time: 0.017 ms
Execution Time: 0.020 ms
(4 rows)

风哥提示:规则和触发器各有优缺点,选择时需要根据具体场景来决定。规则适合简单的查询重写和视图更新,性能较好;触发器适合复杂的业务逻辑和数据验证,更灵活。在实际应用中,合理结合使用两者可以达到最佳效果。同时,要注意规则和触发器的维护成本,保持逻辑清晰,便于后续的维护和调试。

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息