MySQL子查询问题


16

为什么查询

DELETE FROM test 
WHERE id = ( SELECT id 
             FROM (SELECT * FROM test) temp 
             ORDER BY RAND() 
             LIMIT 1
           );

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

如果我以这种形式写:

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

然后正常工作-子查询是否有问题?

Answers:


13

第一个查询不能始终如一地工作的原因与MySQL处理子查询的方式有关。实际上,子查询将经历重写和转换

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

  • Item_in_optimizer
  • Item_in_subselect
  • Item_ref
  • Left_expression_Cache

从发布的示例中,不可能使item_ref成为自引用。就单个DELETE查询而言,测试表作为一个整体不能完全自我引用,因为某些键在转换期间可用,而有些则不可用。因此,当查询执行自引用时,即使实际的自引用表具有该键,键(在这种情况下,id)也可能在转换中消失。

Mysql子查询仅对子SELECT非常有用,甚至多次自引用表也是如此。对于非SELECT查询,不能说相同的话。

我希望这种解释会有所帮助。


7

我认为它无法按预期运行的原因不是MySQL如何处理子查询,而是MySQL如何处理UPDATE语句。该声明:

DELETE 
FROM test 
WHERE id = 
      ( SELECT id 
        FROM 
            ( SELECT * 
              FROM test
            ) temp 
        ORDER BY RAND() 
        LIMIT 1
      ) 

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

  ( SELECT id 
    FROM 
        ( SELECT * 
          FROM test
        ) temp 
    ORDER BY RAND() 
    LIMIT 1
  ) 

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


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

DELETE t
FROM 
      test t
  JOIN 
      ( SELECT id 
        FROM test  
        ORDER BY RAND() 
        LIMIT 1
      ) tmp
    ON tmp.id = t.id

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.