Problem mit der MySQL-Unterabfrage

16

Warum macht diese Abfrage

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

mal 1 zeile löschen, mal 2 zeilen und mal nichts?

Wenn ich es in dieser Form schreibe:

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

dann funktioniert es einwandfrei - liegt ein problem in der unterabfrage vor?

tomas.lang
quelle

Antworten:

13

Der Grund, warum die erste Abfrage nicht konsistent funktioniert, hängt damit zusammen, wie MySQL Unterabfragen verarbeitet. Tatsächlich treten bei Unterabfragen Umschreibungen und Transformationen auf .

Hier werden vier (4) Komponenten erläutert:

  • Item_in_optimizer
  • Item_in_subselect
  • Item_ref
  • Linker_Ausdruck_Cache

In den Beispielen ist es unmöglich, eine item_ref als Selbstreferenz zuzulassen. In Bezug auf Ihre einzelne DELETE-Abfrage kann sich die Testtabelle als Ganzes nicht vollständig selbst referenzieren, da einige Schlüssel während der Transformation verfügbar sind und andere nicht. Wenn eine Abfrage eine Selbstreferenz durchführt, kann daher ein Schlüssel (in diesem Fall id) in einer Umwandlung verschwinden, obwohl die eigentliche selbstreferenzierte Tabelle den Schlüssel enthält.

MySQL-Unterabfragen eignen sich nur für untergeordnete SELECT-Anweisungen, selbst wenn eine Tabelle mehrmals selbst referenziert wird. Dies gilt nicht für Nicht-SELECT-Abfragen.

Ich hoffe diese Erklärung hilft.

RolandoMySQLDBA
quelle
7

Ich denke, der Grund, warum es nicht wie erwartet funktioniert, ist nicht, wie MySQL Unterabfragen verarbeitet, sondern wie MySQL UPDATEAnweisungen verarbeitet . Die Aussage:

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

bearbeitet die WHEREBedingung zeilenweise. Das heißt, für jede Zeile wird die Unterabfrage ausgeführt und das Ergebnis anhand folgender Kriterien getestet id:

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

Daher werden gelegentlich 0, 1, 2 oder sogar mehr Zeilen abgeglichen (und gelöscht)!


Sie könnten es so umschreiben und die Unterabfrage wird einmal verarbeitet:

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

Von der ersten Kugel auf dieser Seite , LIMITwird nicht in mysql Subqueries unterstützt. Ich bin mir nicht sicher, warum es keinen Fehler für Sie auslöst.

Derek Downey
quelle
2
LIMITwird nicht nur für die Verwendung von IN (<code> ersetzt durch backticks ~ drachenstern)
tomas.lang 21.02.11
nun ja ... ich habe etwas gelernt, was erklärt, warum es keinen Fehler warf!
Derek Downey
@ tomas.lang Sie können das Wort mit `(Häkchen) anstelle von <code> -Blöcken umgeben.
Derek Downey