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;

それは正しく動作します-サブクエリに問題はありますか?

回答:


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、またはそれ以上の行と一致(および削除)することがあります!


次のように書き換えると、サブクエリが1回処理されます。

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

1

上の最初の箇条書きからこのページLIMITMySQLのサブクエリではサポートされていません。ただし、エラーが発生しない理由はわかりません。


2
LIMIT使用のみサポートされていませんIN (<code> backticks〜drachensternに置き換えられました)
-tomas.lang

まあ...私は何かを学んだので、なぜそれがエラーを投げなかったのかを説明しています!
デレクダウニー

@ tomas.langでは、<code>ブロックの代わりに、単語を囲む `(チェックマーク)を使用できます。
デレクダウニー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.