MySQL if(cond,a,b)在使用内存表时是否不一致?

时间:2017-08-20 21:45:08

标签: mysql

我在MySQL中查询表时遇到一些不一致的结果。为了演示,我尽可能地删除了代码。

drop table if exists numberfill;
create table numberfill (
    id INT not null primary key auto_increment
) Engine=MEMORY;

drop procedure if exists populate;
DELIMITER $$
CREATE PROCEDURE populate(numberRows INT)
BEGIN
    DECLARE counter INT;
    SET counter = 1;
    WHILE counter <= numberRows DO
        INSERT INTO
            numberfill
        SELECT
            counter;
        SET counter = counter + 1;
    END WHILE;
END
$$
DELIMITER ;

start transaction;
call populate(5000);
commit;

select if(a = 1, 5, 0), if(a = 1, 0, 5)
from (select cast(round(rand()) as unsigned) as a from numberfill) k;

似乎如果我不从numberfill中选择,则查询会给出一致的结果。但是,当我从numberfill表中选择时,我得到的结果是混合的。有些行给出0,0,其他行给出5,5,其他行给出5,0或0,5。

有谁能发现为什么会这样?这是一个MySQL问题还是我做了导致未定义行为的事情?我认为它可能与rand()生成一个浮点数有关。

2 个答案:

答案 0 :(得分:2)

评论太长了。

你得到5, 00, 5的混合物是完全合理的。您正在填写一个包含5,000个数字的表格。然后子查询为每个行分配一个随机数 - 0或1。这应该具体化,因此设置值。

因此,不应该使用您的逻辑来获取0, 05, 5

最新版本的MySQL可能会发现实现子查询不是必要的 - 他们可能会两次评估rand()条件。如果您收到0, 05, 5,那么可能就是这种情况。

答案 1 :(得分:2)

当您多次引用a时会出现此问题。显然,MySql(v 5.7)决定根据其定义重新评估a,即它再次执行rand()以检索a的值。

它与Memory选项无关,也与if函数无关。以下查询将在每行中返回两个不同的值:

select a, a
from (select rand() as a from numberfill) k

您可以通过为变量分配rand()来避免这种情况,并为列别名a选择该变量:

select a, a
from (select @a:=rand() as a from numberfill) k

该查询将始终返回两个值相同的记录。

强制评估实现的另一种方法是设置内部查询的限制(其值大于numberfill中的行数):

select a, a
from (select rand() as a from numberfill limit 9999999999) k

另见Bug #86624, Subquery's RAND() column re-evaluated at every reference