在业务系统开发过程中,随着业务规则不断细化,经常需要处理多表关联计算、条件分支判断、批量数据加工等复杂逻辑,这类场景下的SQL如果直接写在业务代码里,不仅会让代码变得臃肿难维护,还可能因为不合理的写法导致数据库性能下降。通过合理的封装和优化,可以大幅提升复杂SQL的可管理性和执行效率。

复杂业务逻辑的封装方式
1. 存储过程封装
存储过程适合封装需要多步操作、包含事务控制的复杂业务逻辑,比如在订单结算场景中,需要同时更新订单状态、扣减库存、生成结算记录,就可以用存储过程把这些操作整合到一起。
-- 订单结算存储过程示例
CREATE PROCEDURE proc_order_settlement(
IN p_order_id INT,
IN p_user_id INT,
OUT p_result_code INT
)
BEGIN
DECLARE v_stock INT DEFAULT 0;
DECLARE v_order_status INT DEFAULT 0;
-- 开启事务
START TRANSACTION;
-- 查询订单状态和库存
SELECT order_status INTO v_order_status FROM t_order WHERE order_id = p_order_id AND user_id = p_user_id;
SELECT stock INTO v_stock FROM t_goods WHERE goods_id = (SELECT goods_id FROM t_order WHERE order_id = p_order_id);
-- 条件判断逻辑
IF v_order_status != 1 THEN
SET p_result_code = -1; -- 订单状态异常
ROLLBACK;
ELSEIF v_stock <= 0 THEN
SET p_result_code = -2; -- 库存不足
ROLLBACK;
ELSE
-- 更新订单状态
UPDATE t_order SET order_status = 2, settle_time = NOW() WHERE order_id = p_order_id;
-- 扣减库存
UPDATE t_goods SET stock = stock - 1 WHERE goods_id = (SELECT goods_id FROM t_order WHERE order_id = p_order_id);
-- 生成结算记录
INSERT INTO t_settlement(order_id, user_id, settle_amount, create_time)
SELECT p_order_id, p_user_id, order_amount, NOW() FROM t_order WHERE order_id = p_order_id;
SET p_result_code = 0; -- 操作成功
COMMIT;
END IF;
END;
2. 视图封装
视图适合封装高频复用的多表关联查询逻辑,比如需要经常查询用户的基本信息、订单数量、最近下单时间,就可以创建一个用户订单汇总视图,后续查询直接引用视图即可,不需要重复编写关联语句。
-- 用户订单汇总视图示例
CREATE VIEW v_user_order_summary AS
SELECT
u.user_id,
u.user_name,
u.phone,
COUNT(o.order_id) AS order_count,
MAX(o.create_time) AS last_order_time
FROM t_user u
LEFT JOIN t_order o ON u.user_id = o.user_id
GROUP BY u.user_id, u.user_name, u.phone;
3. 自定义函数封装
自定义函数适合封装单一的计算逻辑,比如需要根据用户等级计算折扣比例,就可以创建一个函数,传入用户等级返回对应的折扣值,在SQL中直接调用即可。
-- 用户折扣计算函数示例
CREATE FUNCTION func_calc_user_discount(p_user_level INT)
RETURNS DECIMAL(3,2)
DETERMINISTIC
BEGIN
DECLARE v_discount DECIMAL(3,2) DEFAULT 1.00;
CASE p_user_level
WHEN 1 THEN SET v_discount = 0.95; -- 普通用户95折
WHEN 2 THEN SET v_discount = 0.90; -- 银卡用户9折
WHEN 3 THEN SET v_discount = 0.85; -- 金卡用户85折
ELSE SET v_discount = 1.00; -- 默认无折扣
END CASE;
RETURN v_discount;
END;
复杂SQL的优化实践
1. 索引优化
针对复杂查询中的过滤条件、关联字段、排序字段建立合适的索引是提升性能的核心手段。需要避免全表扫描,同时注意联合索引的最左匹配原则,不要建立过多冗余索引。
比如上面的订单结算存储过程中,t_order表的order_id、user_id、order_status字段经常被查询和过滤,可以建立联合索引:
-- 创建联合索引示例 CREATE INDEX idx_order_id_user_status ON t_order(order_id, user_id, order_status);
2. 查询语句改写优化
复杂SQL经常出现不必要的子查询、多余的关联、重复的计算,可以通过改写提升效率。比如将子查询改为关联查询,去掉不必要的DISTINCT,避免SELECT *只查询需要的字段。
下面的示例是将低效的子查询改写为高效的关联查询:
-- 优化前:子查询方式 SELECT u.user_name, o.order_amount FROM t_user u WHERE u.user_id IN (SELECT user_id FROM t_order WHERE order_status = 2); -- 优化后:关联查询方式 SELECT u.user_name, o.order_amount FROM t_user u INNER JOIN t_order o ON u.user_id = o.user_id WHERE o.order_status = 2;
3. 执行计划分析
通过EXPLAIN命令查看SQL的执行计划,可以定位性能瓶颈,比如是否使用了索引、扫描行数是否过多、是否存在临时表或者文件排序等问题。根据执行计划的反馈调整索引或者改写语句。
-- 查看查询执行计划示例 EXPLAIN SELECT u.user_name, o.order_amount FROM t_user u INNER JOIN t_order o ON u.user_id = o.user_id WHERE o.order_status = 2;
封装与优化的注意事项
- 封装逻辑时要控制粒度,单个存储过程或者函数不要包含过多无关逻辑,避免后期维护困难
- 存储过程中尽量避免编写过于复杂的循环逻辑,大量循环操作会严重影响数据库性能
- 优化时要结合实际数据量和业务场景,不要盲目建立索引,索引过多会影响写入性能
- 封装后的逻辑要做好注释,说明入参、出参、业务逻辑的作用,方便后续开发人员接手
- 线上环境的复杂SQL上线前要做好压测,确认性能符合预期后再正式部署