应优先用declare变量缓存单值中间结果,避免同一条件重复查询;多行多列中间集则用带索引的临时表,二者均需规避函数导致的索引失效。

直接用 DECLARE 变量缓存结果,别让同一条件查两次。
重复查询的典型表现
最常见的就是 WHERE 条件相同、子查询嵌套多次,或者在 IF 判断和后续 SELECT 中用同一条件查表。比如:
IF (SELECT COUNT(*) FROM orders WHERE customer_id = 100) > 5 THEN ...- 紧接着又写
SELECT SUM(amount) FROM orders WHERE customer_id = 100;
这会触发两次全表扫描(没索引时)或两次索引查找(有索引时),执行计划不复用,I/O 和 CPU 都白耗。
用 DECLARE + INTO 一次性取值
把查询结果存到局部变量里,后面逻辑全用变量判断或计算:
DECLARE order_count INT DEFAULT 0; DECLARE total_amount DECIMAL(12,2) DEFAULT 0.0; <p>SELECT COUNT(*), COALESCE(SUM(amount), 0.0) INTO order_count, total_amount FROM orders WHERE customer_id = p_customer_id;</p><p>IF order_count > 5 THEN -- 直接用 order_count 和 total_amount,不再查表 INSERT INTO summary_log VALUES (p_customer_id, order_count, total_amount); END IF;</p>
注意点:
-
INTO后面变量顺序必须和SELECT字段顺序严格一致 - 如果查询可能无结果,
COALESCE()或IFNULL()要包住聚合函数,否则变量会是NULL,影响后续逻辑 - 变量作用域仅限当前存储过程,不用清理,但别重名覆盖输入参数
复杂中间结果该用临时表还是变量?
单值或少量标量(如 count、sum、max id)——用 DECLARE 变量;多行多列中间集(比如要 JOIN 或分组再处理)——上 TEMPORARY TABLE:
CREATE TEMPORARY TABLE temp_top_customers AS SELECT customer_id, SUM(amount) AS total FROM orders WHERE order_date >= DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY customer_id HAVING total > 10000; <p>-- 后续所有操作都基于 temp_top_customers,避免反复算这个聚合 UPDATE customers c JOIN temp_top_customers t ON c.id = t.customer_id SET c.level = 'VIP';</p>
关键区别:
- 变量不能建索引,临时表可以:
CREATE INDEX idx_temp_cid ON temp_top_customers(customer_id); - 临时表生命周期到连接结束,大结果集别忘了
DROP TEMPORARY TABLE temp_top_customers; - 别在循环里反复
CREATE/DROP临时表,开销比建一次用到底高得多
容易被忽略的“隐式重复”
有些重复不是写出来的,是优化器自己搞的:
- 游标里每次
FETCH前都执行一遍SELECT(其实是游标定义语句),但如果你在循环体里又对同一行做额外查询,就真重复了 - 函数调用里含 SQL(比如自定义函数
get_customer_status(id)内部查表),每次调用都走一遍,不如提前查好塞变量里 - 多个
IF分支里各自查同一张表的同一字段,合并成一次查、多个变量接收更稳
最麻烦的是:你以为加了索引就安全,结果 WHERE 条件里用了函数(如 WHERE YEAR(order_date) = 2024),导致索引失效,每次都是全表扫——这种“重复”本质是设计缺陷,得先修查询写法,再谈缓存。











