You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套调用存储过程遇#1137临时表重开错误,如何解决?

解决MySQL临时表无法重复打开的问题

问题背景

你尝试封装包含嵌套存储过程调用的代码时,触发错误:

#1137 - Can't reopen table: 'wm_ewallet_user_balance'

原因在于:调用wm_ewallet_transactions_balance生成临时表wm_ewallet_user_balance后,你在递归CTE中三次引用了该表,但MySQL临时表不允许在同一个查询语句中被多次引用——递归CTE的UNION ALL段和子查询都属于同一查询的一部分,因此触发了这个限制。

解决方案

方案1:复制临时表到新临时表

调用原存储过程后,将临时表数据复制到第二个临时表,后续查询全部使用新表:

SET @p0='87';
CALL `wm_ewallet_transactions_balance`(@p0);

-- 复制临时表结构与数据到新表
CREATE TEMPORARY TABLE wm_ewallet_user_balance_copy LIKE wm_ewallet_user_balance;
INSERT INTO wm_ewallet_user_balance_copy SELECT * FROM wm_ewallet_user_balance;

WITH RECURSIVE rcte(user_id, balance, date) AS (
  (
    SELECT user_id, balance, date
    FROM  wm_ewallet_user_balance_copy
    ORDER BY date
    LIMIT 1
  )
UNION ALL
SELECT COALESCE(t.user_id, r.user_id),
    COALESCE(t.balance, r.balance),
    r.date + INTERVAL 1 DAY
FROM rcte r
LEFT JOIN wm_ewallet_user_balance_copy t ON t.date = r.date + INTERVAL 1 DAY
WHERE r.date < (SELECT MAX(date) FROM wm_ewallet_user_balance_copy)
)
SELECT r.user_id, MIN(r.balance) AS balance, r.date
FROM rcte r
GROUP BY r.user_id, r.date
ORDER BY r.date;

方案2:修改原存储过程返回结果集

将wm_ewallet_transactions_balance改为直接返回查询结果,而非创建临时表,再将结果存入临时表使用:
修改后的存储过程:

DELIMITER //
CREATE PROCEDURE `wm_ewallet_transactions_balance`(IN user_id_param INT)
BEGIN
    SELECT
        id,
        user_id,
        type,
        amount,
        SUM(amount) OVER(PARTITION BY user_id ORDER BY id) AS `balance`,
        created_at,
        DATE(created_at) AS date
    FROM
        wm_ewallet_transactions
    WHERE user_id = user_id_param;
END //
DELIMITER ;

主查询中存入临时表并使用:

SET @p0='87';
-- 将存储过程结果存入临时表
CREATE TEMPORARY TABLE wm_ewallet_user_balance
(
    INDEX user_id_index (user_id),
    INDEX created_at_index (date)
)
AS CALL `wm_ewallet_transactions_balance`(@p0);

-- 后续递归查询同原逻辑,使用该临时表即可
WITH RECURSIVE rcte(user_id, balance, date) AS (
  (
    SELECT user_id, balance, date
    FROM  wm_ewallet_user_balance
    ORDER BY date
    LIMIT 1
  )
UNION ALL
SELECT COALESCE(t.user_id, r.user_id),
    COALESCE(t.balance, r.balance),
    r.date + INTERVAL 1 DAY
FROM rcte r
LEFT JOIN wm_ewallet_user_balance t ON t.date = r.date + INTERVAL 1 DAY
WHERE r.date < (SELECT MAX(date) FROM wm_ewallet_user_balance)
)
SELECT r.user_id, MIN(r.balance) AS balance, r.date
FROM rcte r
GROUP BY r.user_id, r.date
ORDER BY r.date;

方案3:改用普通表(不推荐,仅临时场景可用)

若临时表生命周期无需和会话绑定,可创建普通表存储数据,但需注意定期清理避免冗余:

SET @p0='87';
-- 创建普通表(若不存在)
CREATE TABLE IF NOT EXISTS wm_ewallet_user_balance
(
    INDEX user_id_index (user_id),
    INDEX created_at_index (date)
);
-- 清空旧数据
TRUNCATE TABLE wm_ewallet_user_balance;
-- 修改原存储过程,将结果插入普通表而非临时表
CALL `wm_ewallet_transactions_balance`(@p0);

-- 后续递归查询同原逻辑
WITH RECURSIVE rcte(...) AS (...) SELECT ...;

原理说明

MySQL临时表的核心限制是:同一条SQL语句中只能引用一次临时表。递归CTE的UNION ALL分支、子查询都属于同一查询的组成部分,多次引用会触发无法重新打开的错误。通过复制临时表或改用结果集转存的方式,可绕过这一限制。

内容的提问来源于stack exchange,提问作者Martin AJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 13:01:16