MySQL DELETE FROM mit Unterabfrage als Bedingung

83

Ich versuche eine Abfrage wie folgt durchzuführen:

DELETE FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN (
    SELECT DISTINCT(th1.tid)
    FROM term_hierarchy AS th1
    INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
    WHERE th1.parent = 1015
);

Wie Sie wahrscheinlich sehen können, möchte ich die Elternbeziehung zu 1015 löschen, wenn dieselbe Nachricht andere Eltern hat. Dies führt jedoch zu einem Syntaxfehler:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS th
WHERE th.parent = 1015 AND th.tid IN (
  SELECT DISTINCT(th1.tid)
  FROM ter' at line 1

Ich habe die Dokumentation überprüft und die Unterabfrage selbst ausgeführt, und alles scheint zu überprüfen. Kann jemand herausfinden, was hier falsch ist?

Update : Wie unten beantwortet, erlaubt MySQL nicht, dass die Tabelle, aus der Sie löschen, in einer Unterabfrage für die Bedingung verwendet wird.

mikl
quelle
2
Achtung : Gute Antwort unten stackoverflow.com/a/4471359/956397 Fügen Sie einfach den Tabellenalias nachDELETE t FROM table t ...
PiTheNumber

Antworten:

37

Sie können keine Zieltabelle zum Löschen angeben.

Eine Problemumgehung

create table term_hierarchy_backup (tid int(10)); <- check data type

insert into term_hierarchy_backup 
SELECT DISTINCT(th1.tid)
FROM term_hierarchy AS th1
INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th1.parent = 1015;

DELETE FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN (select tid from term_hierarchy_backup);
ajreal
quelle
wir haben beide recht - siehe seinen Kommentar zu meiner Antwort unten. Alias ​​Syntax und Logik waren beide Probleme :)
JNK
Ja, das Löschen über eine Unterabfrage scheint derzeit in MySQL nicht möglich zu sein - danke, dass Sie sich das angesehen haben :)
mikl
Hat das "DELETE FROM term_hierarchy AS th" in dieser letzten Zeile nicht das gleiche Problem? Ich erhalte einen Syntaxfehler wie beim OP.
Malhal
Sie sollten term_hierarchy_backup.tid einen Index hinzufügen.
Roman Newaza
1
Ich kann das überprüfen, dass es 2019 auf MariaDB 10.3.14 oder MySQL Community Server 5.7.27
alpham8
277

Für andere, die diese Frage während der Verwendung einer Unterabfrage löschen möchten, überlasse ich Ihnen dieses Beispiel, um MySQL zu überlisten (auch wenn einige Leute glauben, dass dies nicht möglich ist):

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM tableE
             WHERE arg = 1 AND foo = 'bar');

gibt Ihnen einen Fehler:

ERROR 1093 (HY000): You can't specify target table 'e' for update in FROM clause

Allerdings diese Abfrage:

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM (SELECT id
                   FROM tableE
                   WHERE arg = 1 AND foo = 'bar') x);

wird gut funktionieren:

Query OK, 1 row affected (3.91 sec)

Schließen Sie Ihre Unterabfrage in eine zusätzliche Unterabfrage (hier mit dem Namen x) ein, und MySQL erledigt gerne das, was Sie verlangen.

CodeReaper
quelle
10
Es hat einige Zeit gedauert, aber ich habe es zum Laufen gebracht. Wichtig: 1) Die erste Tabelle muss wie hier gezeigt mit "e" aliasiert werden. 2) Das "x" am Ende ist kein Platzhalter, sondern der Alias ​​für die von der Unterabfrage erzeugte temporäre Tabelle "(SELECT id FROM tableE) WO arg = 1 UND foo = 'bar') ".
Tilman Hausherr
3
Warum funktioniert das? Das ändert sich sehr für mich, aber darüber hinaus sollte es nicht funktionieren. Es funktioniert , sollte es aber nicht.
donatJ
1
unglaublich. das funktioniert tatsächlich! Sie sind jedoch nicht gezwungen, die Tabelle mit e zu aliasen. Sie können einen beliebigen Alias ​​verwenden.
Andrei Sandulescu
1
@jakabadambalazs Meine Argumentation bei der Erstellung war, dass die Unterabfrage, die mit "SELECT id" beginnt, eine Liste von IDs beendet und zurückgibt und daher die Sperre der Tabelle aufhebt, aus der Sie löschen möchten.
CodeReaper
8
@jakabadambalazs: Wir können nicht dieselbe Tabelle ( e) in einem DELETE und in seinem Sub-SELECT verwenden. Wir können jedoch ein Sub-Sub-SELECT verwenden, um eine temporäre Tabelle ( x) zu erstellen , und dieses für das Sub-SELECT verwenden.
Steve Almond
36

Der Alias ​​sollte nach dem DELETESchlüsselwort stehen:

DELETE th
FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN 
(
    SELECT DISTINCT(th1.tid)
    FROM term_hierarchy AS th1
    INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
    WHERE th1.parent = 1015
);
James Wiseman
quelle
3
Das ist eine gute Antwort. Richtiges Aliasing wird einen großen Beitrag zur Lösung von Problemen leisten, die dem ursprünglichen Beitrag ähneln. (wie meine.)
Usumoio
8

Sie müssen in der delete-Anweisung erneut auf den Alias ​​verweisen, z.

DELETE th FROM term_hierarchy AS th
....

Wie hier in MySQL-Dokumenten beschrieben.

JNK
quelle
geht es nicht um Alias, bitte überprüfen Sie das OP noch einmal
Ajreal
@ajreal - Ich habe es getan, und bitte beachten Sie, dass der Fehler bei der Aliasdefinition beginnt. In der MySQL-Dokumentation wird ausdrücklich angegeben, dass Sie den Alias ​​in der DELETE-Anweisung sowie in der FROM-Klausel verwenden müssen. Vielen Dank für die Ablehnung.
JNK
Mach delete from your_table as t1 where t1.id in(select t2.id from your_table t2);das einfach, was hast du bekommen?
Ajreal
4
In der Dokumentation ist klar angegeben; Currently, you cannot delete from a table and select from the same table in a subquery. dev.mysql.com/doc/refman/5.5/en/delete.html
Björn
1
Sie müssen den Alias ​​nicht reparieren, geben Sie einfach keine
Zieltabelle
6

Ich ging das etwas anders an und es funktionierte für mich;

Ich musste secure_linksaus meiner Tabelle entfernen , die auf die conditionsTabelle verwies, in der keine Bedingungszeilen mehr vorhanden waren. Im Grunde genommen ein Housekeeping-Skript. Dies gab mir den Fehler - Sie können keine Zieltabelle zum Löschen angeben.

Als ich hier nach Inspiration suchte, kam ich auf die folgende Abfrage und sie funktioniert einwandfrei. Dies liegt daran, dass eine temporäre Tabelle erstellt sl1wird, die als Referenz für DELETE verwendet wird.

DELETE FROM `secure_links` WHERE `secure_links`.`link_id` IN 
            (
            SELECT
                `sl1`.`link_id` 
            FROM 
                (
                SELECT 

                    `sl2`.`link_id` 

                FROM 
                    `secure_links` AS `sl2` 
                    LEFT JOIN `conditions` ON `conditions`.`job` = `sl2`.`job` 

                WHERE 

                    `sl2`.`action` = 'something' AND 
                    `conditions`.`ref` IS NULL 
                ) AS `sl1`
            )

Funktioniert bei mir.

Darren Edwards
quelle
5

Ist die "in" -Klausel in der Löschung nicht ... äußerst ineffizient, wenn eine große Anzahl von Werten von der Unterabfrage zurückgegeben wird? Sie sind sich nicht sicher, warum Sie nicht einfach eine innere (oder rechte) Verknüpfung mit der ursprünglichen Tabelle aus der zu löschenden Unterabfrage auf der ID herstellen möchten, anstatt uns die "in (Unterabfrage)".?

DELETE T FROM Target AS T
RIGHT JOIN (full subquery already listed for the in() clause in answers above) ` AS TT ON (TT.ID = T.ID)

Und vielleicht wird es in "MySQL erlaubt es nicht" beantwortet, aber es funktioniert gut für mich, vorausgesetzt, ich stelle sicher, dass ich vollständig kläre, was gelöscht werden soll (T AUS Ziel AS T LÖSCHEN). Löschen mit Join in MySQL verdeutlicht das Problem DELETE / JOIN.

Jeff
quelle
2

Wenn Sie dies mit 2 Abfragen tun möchten, können Sie immer etwas Ähnliches tun:

1) Nehmen Sie die IDs vom Tisch mit:

SELECT group_concat(id) as csv_result FROM your_table WHERE whatever = 'test' ...

Kopieren Sie dann das Ergebnis mit Maus / Tastatur oder Programmiersprache nach XXX:

2) DELETE FROM your_table WHERE id IN ( XXX )

Vielleicht könnten Sie dies in einer Abfrage tun, aber das ist, was ich bevorzuge.

TomoMiha
quelle
0

@CodeReaper, @BennyHill: Es funktioniert wie erwartet.

Ich frage mich jedoch, wie zeitaufwändig es ist, Millionen von Zeilen in der Tabelle zu haben. Anscheinend dauerte 5msdie Ausführung ungefähr 5k Datensätze in einer korrekt indizierten Tabelle.

Meine Frage:

SET status = '1'
WHERE id IN (
    SELECT id
    FROM (
      SELECT c2.id FROM clusters as c2
      WHERE c2.assign_to_user_id IS NOT NULL
        AND c2.id NOT IN (
         SELECT c1.id FROM clusters AS c1
           LEFT JOIN cluster_flags as cf on c1.last_flag_id = cf.id
           LEFT JOIN flag_types as ft on ft.id = cf.flag_type_id
         WHERE ft.slug = 'closed'
         )
      ) x)```

Or is there something we can improve on my query above?
rc.adhikari
quelle
0

Sie können den Alias ​​auf diese Weise für die delete-Anweisung verwenden

DELETE  th.*
FROM term_hierarchy th
INNER JOIN term_hierarchy th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th.parent = 1015;
YakovGdl35
quelle