一、为什么需要存储过程、函数与触发器?
当业务变得越来越复杂,单纯靠应用程序代码处理所有逻辑会显得力不从心。想象一下,每次用户下单都需要在多个表里插入数据、更新库存、计算折扣,如果这些操作都在应用层完成,不仅代码臃肿,性能也会受影响。这时候,数据库层面的逻辑处理能力就显得尤为重要。
PolarDB作为阿里云推出的云原生数据库,提供了强大的存储过程、函数和触发器支持。它们就像是预装在数据库里的"小程序",可以帮我们完成很多重复性工作。举个例子,电商平台的订单状态变更时自动发送通知,这种场景用触发器就再合适不过了。
二、存储过程:数据库里的"批处理脚本"
存储过程像是一个预编译好的脚本,可以接受参数,包含一系列SQL语句和控制逻辑。它的最大优势是减少网络传输,因为所有操作都在数据库内部完成。
-- 技术栈:PolarDB PostgreSQL兼容版
-- 创建一个处理订单的存储过程
CREATE OR REPLACE PROCEDURE process_order(
p_user_id INT,
p_product_id INT,
p_quantity INT
)
LANGUAGE plpgsql
AS $$
DECLARE
v_stock INT;
v_price DECIMAL(10,2);
v_order_id INT;
BEGIN
-- 检查库存
SELECT stock, price INTO v_stock, v_price
FROM products WHERE id = p_product_id;
IF v_stock < p_quantity THEN
RAISE EXCEPTION '库存不足,当前库存: %', v_stock;
END IF;
-- 创建订单
INSERT INTO orders(user_id, order_date, status)
VALUES (p_user_id, CURRENT_TIMESTAMP, '待付款')
RETURNING id INTO v_order_id;
-- 添加订单明细
INSERT INTO order_items(order_id, product_id, quantity, unit_price)
VALUES (v_order_id, p_product_id, p_quantity, v_price);
-- 更新库存
UPDATE products SET stock = stock - p_quantity
WHERE id = p_product_id;
COMMIT;
RAISE NOTICE '订单 % 创建成功', v_order_id;
END;
$$;
-- 调用存储过程
CALL process_order(123, 456, 2);
这个例子展示了存储过程的典型用法:封装业务逻辑、减少网络往返、保证操作原子性。适合用在订单处理、数据批量导入等场景。
三、函数:可重用的计算单元
函数和存储过程很像,但主要区别是函数必须有返回值,且通常不修改数据库状态。它们特别适合封装复杂计算逻辑。
-- 技术栈:PolarDB PostgreSQL兼容版
-- 创建一个计算折扣价格的函数
CREATE OR REPLACE FUNCTION calculate_discount(
original_price DECIMAL(10,2),
user_level VARCHAR(20),
promotion_active BOOLEAN
)
RETURNS DECIMAL(10,2)
LANGUAGE plpgsql
AS $$
DECLARE
discount_rate DECIMAL(3,2);
final_price DECIMAL(10,2);
BEGIN
-- 基础折扣率
discount_rate := 1.0;
-- 根据用户等级调整
CASE user_level
WHEN 'VIP' THEN discount_rate := 0.85;
WHEN 'GOLD' THEN discount_rate := 0.9;
WHEN 'SILVER' THEN discount_rate := 0.95;
ELSE discount_rate := 1.0;
END CASE;
-- 促销活动额外折扣
IF promotion_active THEN
discount_rate := discount_rate * 0.95;
END IF;
-- 计算最终价格
final_price := original_price * discount_rate;
-- 确保不低于成本价
IF final_price < original_price * 0.6 THEN
final_price := original_price * 0.6;
END IF;
RETURN ROUND(final_price, 2);
END;
$$;
-- 使用函数
SELECT calculate_discount(100.00, 'VIP', true);
函数非常适合价格计算、数据转换、条件判断等场景。在PolarDB中,函数还可以用在SQL语句的SELECT、WHERE等子句中,非常灵活。
四、触发器:自动化的数据库"看门人"
触发器是在特定数据库事件(INSERT/UPDATE/DELETE)发生时自动执行的特殊存储过程。它们像是数据库的"自动应答机"。
-- 技术栈:PolarDB PostgreSQL兼容版
-- 创建一个订单日志触发器
CREATE OR REPLACE FUNCTION log_order_changes()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO order_audit_log(
order_id,
action,
change_time,
new_status
)
VALUES (
NEW.id,
'创建订单',
CURRENT_TIMESTAMP,
NEW.status
);
ELSIF TG_OP = 'UPDATE' THEN
IF NEW.status <> OLD.status THEN
INSERT INTO order_audit_log(
order_id,
action,
change_time,
old_status,
new_status
)
VALUES (
NEW.id,
'更新订单状态',
CURRENT_TIMESTAMP,
OLD.status,
NEW.status
);
END IF;
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO order_audit_log(
order_id,
action,
change_time
)
VALUES (
OLD.id,
'删除订单',
CURRENT_TIMESTAMP
);
END IF;
RETURN NEW;
END;
$$;
-- 在orders表上创建触发器
CREATE TRIGGER order_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW
EXECUTE FUNCTION log_order_changes();
触发器最适合用于审计日志、数据同步、业务规则验证等场景。但要注意,过多或过于复杂的触发器会影响性能。
五、高级特性与性能优化
PolarDB对这些特性做了很多优化。比如存储过程支持预编译,执行计划会被缓存;函数可以标记为IMMUTABLE(不依赖数据库状态)或STABLE(结果在事务内不变),帮助优化器生成更好的执行计划。
-- 技术栈:PolarDB PostgreSQL兼容版
-- 创建一个优化标记的函数
CREATE OR REPLACE FUNCTION format_phone_number(phone TEXT)
RETURNS TEXT
LANGUAGE sql
IMMUTABLE -- 标记为不可变函数
AS $$
SELECT regexp_replace(phone, '(\d{3})(\d{4})(\d{4})', '\1****\3');
$$;
-- 这个函数可以被优化器内联处理
SELECT * FROM users WHERE format_phone_number(phone) = '138****1234';
对于频繁调用的简单逻辑,还可以使用SQL语言函数,它们通常比PL/pgSQL函数执行效率更高。
六、应用场景与技术选型建议
存储过程最适合:
- 复杂的多步数据操作
- 需要事务控制的批量处理
- 对性能敏感的核心业务逻辑
函数最适合:
- 数据转换和计算
- 可重用的业务规则
- SQL查询中的复杂条件
触发器最适合:
- 数据变更审计
- 跨表数据同步
- 简单的业务规则实施
注意事项:
- 避免在触发器中执行耗时操作
- 复杂的业务逻辑优先考虑用存储过程
- 函数尽量设计为无副作用
- 注意错误处理和事务控制
七、总结
PolarDB的存储过程、函数和触发器为处理复杂业务逻辑提供了强大工具。它们能让数据操作更高效,业务逻辑更集中,代码更简洁。合理使用这些特性,可以显著提升应用性能和可维护性。
记住,没有银弹。存储过程适合封装数据库密集型的操作,但不应滥用。现代应用架构中,业务逻辑的放置位置需要综合考虑性能、维护性和团队技能等因素。PolarDB提供的这些特性给了我们更多选择,让数据库不再是简单的数据存储,而是能主动参与业务处理的智能数据平台。
评论