Masalah dengan subquery MySQL


16

Kenapa kueri ini

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

kadang menghapus 1 baris, kadang 2 baris dan kadang tidak sama sekali?

Jika saya menulisnya di formulir ini:

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

lalu bekerja dengan benar - apakah ada masalah dalam subquery?

Jawaban:


13

Alasan kueri pertama tidak berfungsi secara konsisten terkait dengan cara MySQL memproses subquery. Bahkan, subqueries akan mengalami penulisan ulang dan transformasi .

Ada empat (4) komponen yang dijelaskan di sini:

  • Item_in_optimizer
  • Item_in_subselect
  • Item_ref
  • Left_expression_Cache

Dari contoh yang diposting, tidak mungkin untuk membiarkan item_ref menjadi referensi mandiri. Dalam hal permintaan DELETE tunggal Anda, tabel pengujian secara keseluruhan tidak dapat sepenuhnya merujuk dirinya sendiri karena beberapa kunci tersedia selama transformasi dan beberapa tidak. Oleh karena itu, ketika kueri melakukan referensi-diri, kunci (dalam hal ini id) dapat menghilang dalam transformasi meskipun tabel referensi-sendiri yang sebenarnya memiliki kunci.

Subquery Mysql hanya bagus untuk sub-SELECT, bahkan merujuk sendiri tabel beberapa kali. Hal yang sama tidak dapat dikatakan untuk permintaan non-SELECT.

Saya harap penjelasan ini membantu.


7

Saya pikir alasan mengapa itu tidak berfungsi seperti yang diharapkan bukanlah bagaimana MySQL memproses subqueries tetapi bagaimana MySQL memproses UPDATEpernyataan. Pernyataan:

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

akan memproses WHEREkondisi baris demi baris. Artinya, untuk setiap baris, itu akan menjalankan subquery dan menguji hasilnya terhadap id:

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

Jadi, itu sesekali akan cocok (dan menghapus) 0, 1, 2 atau bahkan lebih banyak baris!


Anda dapat menulis ulang seperti ini dan subquery akan diproses sekali:

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

1

Dari butir pertama di halaman ini , LIMITtidak didukung di subqueries mysql. Saya tidak yakin mengapa itu tidak membuat kesalahan untuk Anda.


2
LIMITtidak didukung hanya untuk menggunakan IN (<code> diganti dengan backticks ~ drachenstern)
tomas.lang

baik ... saya belajar sesuatu, yang menjelaskan mengapa itu tidak melemparkan kesalahan!
Derek Downey

@ tomas.lang Anda dapat menggunakan `(tanda centang) di sekitar kata, alih-alih blok <code>.
Derek Downey
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.