Problema con la subquery MySQL


16

Perché questa query

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

a volte cancellare 1 riga, a volte 2 righe e a volte niente?

Se lo scrivo in questo modulo:

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

quindi funziona correttamente - è un problema nella sottoquery?

Risposte:


13

Il motivo per cui la prima query non funziona in modo coerente ha a che fare con il modo in cui MySQL elabora le subquery. In effetti, le subquery subiranno riscritture e trasformazioni .

Ci sono quattro (4) componenti spiegati qui:

  • Item_in_optimizer
  • Item_in_subselect
  • Item_ref
  • Left_expression_Cache

Dagli esempi pubblicati, sarebbe impossibile consentire a item_ref di diventare un riferimento automatico. In termini di singola query DELETE, la tabella di test nel suo insieme non può autoregolarsi completamente poiché alcune chiavi sono disponibili durante la trasformazione e altre no. Pertanto, quando una query esegue un riferimento automatico, una chiave (in questo caso id) può scomparire in una trasformazione anche se la tabella autoreferenziata effettiva ha la chiave.

Le subquery Mysql sono ottime solo per i sub-SELECT, anche facendo riferimento a una tabella più volte. Lo stesso non si può dire per le query non SELECT.

Spero che questa spiegazione sia d'aiuto.


7

Penso che il motivo per cui non funziona come previsto non è come MySQL elabora le subquery ma come MySQL elabora le UPDATEdichiarazioni. La dichiarazione:

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

elaborerà la WHEREcondizione riga per riga. Significato, per ogni riga, eseguirà la subquery e testerà il risultato rispetto a id:

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

Quindi, occasionalmente corrisponderà (ed eliminerà) 0, 1, 2 o anche più righe!


È possibile riscriverlo in questo modo e la subquery verrà elaborata una volta:

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

1

Dal primo punto elenco in questa pagina , LIMITnon è supportato nelle subquery mysql. Non sono sicuro del perché non ti dia un errore, però.


2
LIMITnon è supportato solo per l'utilizzo IN (<code> sostituito con backticks ~ drachenstern)
tomas.lang

bene ... ho imparato qualcosa, il che spiega perché non ha generato un errore!
Derek Downey,

@ tomas.lang puoi usare `(segni di spunta) che circondano la parola, anziché i blocchi <code>.
Derek Downey,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.