1、优化方式
(1)使用合适的索引
确保存储过程中使用的查询都能利用到合适的索引。索引可以显著提高查询的查找速度。
(2)避免不必要的复杂计算
尽量避免在存储过程中进行不必要的复杂计算,可以将一些重复计算提前在应用层预处理。
(3)减少临时表的使用
虽然临时表有时能简化处理,但频繁的创建和删除临时表会增加开销。尽量减少临时表的使用,或者使用适当的索引和优化查询来替代。
(4)避免使用游标
游标会逐行处理数据,这在大数据量时性能较差。尽量使用集合操作替代游标。
(5)合理使用批量操作
将多次单条记录操作合并为一次批量操作,减少SQL执行次数,降低网络和数据库的开销。
(6)提高并行度
将一些可以并行执行的任务分离出来,充分利用多核CPU的优势。
2、示例
(1)使用合适的索引
-- 创建索引
CREATE INDEX idx_user_email ON users(email);
-- 优化前的存储过程
DELIMITER //
CREATE PROCEDURE GetUserByEmail(IN userEmail VARCHAR(255))
BEGIN
SELECT * FROM users WHERE email = userEmail;
END //
DELIMITER ;
-- 优化后的存储过程(使用索引)
DELIMITER //
CREATE PROCEDURE GetUserByEmail(IN userEmail VARCHAR(255))
BEGIN
SELECT * FROM users WHERE email = userEmail;
END //
DELIMITER ;
(2)避免不必要的复杂计算
-- 优化前的存储过程:
DELIMITER //
CREATE PROCEDURE CalculateTotalSalary(IN departmentId INT)
BEGIN
DECLARE totalSalary DECIMAL(10, 2);
SELECT SUM(salary) INTO totalSalary
FROM employees
WHERE department_id = departmentId;
SELECT totalSalary, totalSalary * 0.2 AS bonus
FROM dual;
END //
DELIMITER ;
-- 优化后的存储过程(避免重复计算):
DELIMITER //
CREATE PROCEDURE CalculateTotalSalary(IN departmentId INT)
BEGIN
DECLARE totalSalary DECIMAL(10, 2);
DECLARE bonus DECIMAL(10, 2);
SELECT SUM(salary) INTO totalSalary
FROM employees
WHERE department_id = departmentId;
SET bonus = totalSalary * 0.2;
SELECT totalSalary, bonus FROM dual;
END //
DELIMITER ;
(3)减少临时表的使用
-- 优化前的存储过程:
DELIMITER //
CREATE PROCEDURE GetHighSalaryEmployees()
BEGIN
CREATE TEMPORARY TABLE TempEmployees AS
SELECT * FROM employees WHERE salary > 100000;
SELECT * FROM TempEmployees;
DROP TEMPORARY TABLE TempEmployees;
END //
DELIMITER ;
-- 优化后的存储过程(直接查询):
DELIMITER //
CREATE PROCEDURE GetHighSalaryEmployees()
BEGIN
SELECT * FROM employees WHERE salary > 100000;
END //
DELIMITER ;
(4)避免使用游标
-- 优化前的存储过程:
DELIMITER //
CREATE PROCEDURE SumSalaries()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE emp_salary DECIMAL(10, 2);
DECLARE totalSalary DECIMAL(10, 2) DEFAULT 0;
DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN emp_cursor;
read_loop: LOOP
FETCH emp_cursor INTO emp_salary;
IF done THEN
LEAVE read_loop;
END IF;
SET totalSalary = totalSalary + emp_salary;
END LOOP;
CLOSE emp_cursor;
SELECT totalSalary;
END //
DELIMITER ;
-- 优化后的存储过程(使用集合操作):
DELIMITER //
CREATE PROCEDURE SumSalaries()
BEGIN
DECLARE totalSalary DECIMAL(10, 2);
SELECT SUM(salary) INTO totalSalary FROM employees;
SELECT totalSalary;
END //
DELIMITER ;
(5)合理使用批量操作
-- 优化前的存储过程:
DELIMITER //
CREATE PROCEDURE InsertEmployees(IN employeeList JSON)
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE total INT;
SET total = JSON_LENGTH(employeeList);
WHILE i < total DO
INSERT INTO employees (name, salary)
VALUES (JSON_UNQUOTE(JSON_EXTRACT(employeeList, CONCAT('$[', i, '].name'))),
JSON_UNQUOTE(JSON_EXTRACT(employeeList, CONCAT('$[', i, '].salary'))));
SET i = i + 1;
END WHILE;
END //
DELIMITER ;
-- 优化后的存储过程(批量插入):
DELIMITER //
CREATE PROCEDURE InsertEmployees(IN employeeList JSON)
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE total INT;
DECLARE name VARCHAR(255);
DECLARE salary DECIMAL(10, 2);
SET total = JSON_LENGTH(employeeList);
INSERT INTO employees (name, salary)
SELECT JSON_UNQUOTE(JSON_EXTRACT(employeeList, CONCAT('$[', number, '].name'))),
JSON_UNQUOTE(JSON_EXTRACT(employeeList, CONCAT('$[', number, '].salary')))
FROM (
SELECT @rownum := @rownum + 1 AS number
FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t2,
(SELECT @rownum := 0) t3
LIMIT total
) numbers;
END //
DELIMITER ;