MySQL子查询的问题

tom*_*ang 16 mysql

为什么这个查询

DELETE FROM test 
WHERE id = ( SELECT id 
             FROM (SELECT * FROM test) temp 
             ORDER BY RAND() 
             LIMIT 1
           );
Run Code Online (Sandbox Code Playgroud)

有时删除 1 行,有时删除 2 行,有时什么都不删除?

如果我用这种形式写:

SET @var = ( SELECT id 
             FROM (SELECT * FROM test) temp 
             ORDER BY RAND() 
             LIMIT 1
           ); 
DELETE FROM test 
WHERE id=@var;
Run Code Online (Sandbox Code Playgroud)

那么它可以正常工作 - 子查询有问题吗?

Rol*_*DBA 13

第一个查询不一致的原因与 MySQL 处理子查询的方式有关。事实上,子查询会经历重写和转换

这里解释了四 (4) 个组件:

  • item_in_optimizer
  • item_in_subselect
  • item_ref
  • Left_expression_Cache

从发布的示例来看,不可能允许 item_ref 成为自我引用。就您的单个 DELETE 查询而言,整个测试表无法完全自引用自身,因为某些键在转换过程中可用,而有些则不可用。因此,当查询执行自引用时,即使实际自引用表具有键,键(在本例中为 id)也会在转换中消失。

Mysql 子查询仅适用于子 SELECT,甚至多次自引用表。对于非 SELECT 查询则不能这样说。

我希望这个解释有帮助。


ype*_*eᵀᴹ 8

我认为它没有按预期工作的原因不是 MySQL 如何处理子查询,而是 MySQL 如何处理UPDATE语句。该声明:

DELETE 
FROM test 
WHERE id = 
      ( SELECT id 
        FROM 
            ( SELECT * 
              FROM test
            ) temp 
        ORDER BY RAND() 
        LIMIT 1
      ) 
Run Code Online (Sandbox Code Playgroud)

WHERE逐行处理条件。意思是,对于每一行,它将运行子查询并针对id以下结果测试结果:

  ( SELECT id 
    FROM 
        ( SELECT * 
          FROM test
        ) temp 
    ORDER BY RAND() 
    LIMIT 1
  ) 
Run Code Online (Sandbox Code Playgroud)

因此,它偶尔会匹配(并删除)0、1、2 甚至更多行!


您可以像这样重写它,并且子查询将被处理一次:

DELETE t
FROM 
      test t
  JOIN 
      ( SELECT id 
        FROM test  
        ORDER BY RAND() 
        LIMIT 1
      ) tmp
    ON tmp.id = t.id
Run Code Online (Sandbox Code Playgroud)