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?

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.


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

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.


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.