微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

MySQL 5.5.56 临时表在触发器中使用时始终为空,但在手动运行查询时有效

如何解决MySQL 5.5.56 临时表在触发器中使用时始终为空,但在手动运行查询时有效

有人可以帮我或帮我清理一下。我有这个 sql 代码,我需要在一个不起作用的触发器上运行它。但在 sql 客户端上手动运行代码时有效。

SET @sr_id = NEW.purchase_id; /* SET @sr_id = 123456 when run manually */
SET @ndi = (SELECT COUNT(a.id) FROM purchase_rewards a LEFT JOIN item b ON b.id = a.item_id WHERE a.unit_id IS NOT NULL AND COALESCE(b.is_privileged,0) = 0 AND a.purchase_id = @sr_id);
SET @res = @ndi - CEIL(@ndi/2);

DROP TEMPORARY TABLE IF EXISTS for_removal;
CREATE TEMPORARY TABLE for_removal
    SELECT ID FROM (
        SELECT a.id,@rownum := @rownum + 1 AS `rank` FROM purchase_rewards a LEFT JOIN item b ON b.id = a.item_id
            WHERE a.purchase_id = @sr_id AND COALESCE(b.is_privileged,0) = 0
        ) ft CROSS JOIN (SELECT @rownum := 0) r WHERE `rank` <= @res;
        
DELETE ta FROM purchase_rewards ta INNER JOIN for_removal tb ON ta.id = tb.id WHERE ta.purchase_id = @sr_id;

代码查询购买的非“特权”商品,在每个商品上放置一个排名列并删除其中的一半。你只得到一半的奖励,这就是重点。该软件是由其他人创建的,没有源代码,因此这是一个搭载系统。

我在每个代码之间放置了调试代码,以查看连接是否发生变化或结果为空,但除最后一部分外一切正常。在删除部分之前我添加了调试代码

SET @icount = (SELECT COUNT(ID) FROM for_removal);
INSERT INTO debug_log SET `log` = @icount;

结果是表总是空的。我也尝试将代码转换为存储过程,但我遇到了同样的问题。仅在其工作的地方手动运行代码

我目前正在解决 CURSOR 和循环删除的问题,但它在有数百个项目时会变慢。

样本数据:dbfiddle

谢谢!

解决方法

根据上面的评论,答案是在查询之前设置 @rownum 变量。

SET @rownum = 0;
CREATE TEMPORARY TABLE ...

原因是在CROSS JOIN中不能依赖表求值的顺序。如果在@rownum 初始化之前评估子查询,那么@rownum 将为NULL,并且任何使用@rownum := @rownum + 1 增加它的尝试也将产生NULL。所以 rank 的每一行都会有 NULL,并且没有行满足 WHERE 子句。

至于为什么这在 MySQL 客户端中有效但在触发器中无效,我有一个理论:

如果您多次测试查询,会话变量 @rownum 将保持其值。因此,如果您在一个会话中将其设置为某个非 NULL 值,然后随后在同一会话中测试排名查询,它将递增。

但如果您将其作为触发器的一部分运行,则每次都可能是一个全新的会话,并且 @rownum 的值最初将为 NULL。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。