存储过程
存储过程是一段预编译的、存储在数据库服务端的 SQL 代码块,可接收参数、包含流程控制(IF/LOOP),一次定义、多次调用。
存储过程的优势:
- 代码复用性:同一个存储过程可以被多个应用程序调用
- 性能提升:预编译后执行速度更快,减少网络传输(一次调用替代多条 SQL 往返)
- 数据安全:可以设置用户权限只允许执行特定存储过程,限制直接访问表
- 易于维护:更新存储过程后,所有调用方自动使用新版本
- 业务逻辑封装:将复杂逻辑封装在数据库层
但也有缺点:业务逻辑嵌入数据库导致分层不清晰、不同数据库的语法不兼容、调试不便、版本管理麻烦。
现代化架构倾向于把业务逻辑放在应用层,但对数据密集型操作(如复杂报表计算),存储过程仍然很实用。
不同数据库的语法差异
| 数据库 | 创建语法 | 调用语法 | 参数标记 |
|---|---|---|---|
| MySQL | CREATE PROCEDURE | CALL procedure_name() | IN/OUT/INOUT |
| SQL Server | CREATE PROCEDURE | EXEC procedure_name | @param_name |
| PostgreSQL | CREATE FUNCTION | SELECT procedure_name() | $1, $2 ... |
| Oracle | CREATE PROCEDURE | EXEC procedure_name | :param_name |
删除存储过程
-- MySQL
DROP PROCEDURE IF EXISTS calc_actual_salary;
-- SQL Server
DROP PROCEDURE IF EXISTS calc_actual_salary;
IF EXISTS可以确保存储过程不存在时不会报错。
多参数存储过程示例
-- SQL Server 风格的多参数存储过程
CREATE PROCEDURE GetEmployeesByDeptAndYear
@DeptCode INT,
@JoinYear INT
AS
BEGIN
SELECT emp_name, basic_salary, performance_bonus
FROM employees
WHERE dept_code = @DeptCode AND join_year = @JoinYear;
END;
-- 调用
EXEC GetEmployeesByDeptAndYear @DeptCode = 2, @JoinYear = 2018;
以飞翔科技为例。一个计算员工实发工资的存储过程:
DELIMITER //
CREATE PROCEDURE calc_actual_salary(IN emp_id_val INT, OUT actual_salary DECIMAL(10,2))
BEGIN
DECLARE base_salary DECIMAL(10,2);
DECLARE bonus DECIMAL(10,2);
DECLARE insurance DECIMAL(10,2);
DECLARE tax DECIMAL(10,2);
-- 查询员工的各项数据
SELECT basic_salary, performance_bonus, social_security, personal_tax
INTO base_salary, bonus, insurance, tax
FROM employees
WHERE emp_id = emp_id_val;
-- 计算实发:基本工资 + 奖金 - 社保 - 个税
SET actual_salary = base_salary + IFNULL(bonus, 0) - IFNULL(insurance, 0) - IFNULL(tax, 0);
END //
DELIMITER ;
-- 调用存储过程:计算翱翔的实发工资
CALL calc_actual_salary(10001, @result);
SELECT @result AS 实发工资;
-- 输出:8888.88 + 18888.88 - 2320.00 - 1666.66 = 23791.10
这个存储过程封装了工资计算逻辑:应用层只需传员工 ID,数据库内部完成四步查询和计算,网络往返从"多次查询 + 应用层计算"压缩为一次 CALL。