web-dev-qa-db-fra.com

MySQL DELETE FROM avec sous-requête comme condition

J'essaie de faire une requête comme celle-ci:

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
);

Comme vous pouvez probablement le constater, je souhaite supprimer la relation parent en 1015 si le même groupe a d'autres parents. Cependant, cela me donne une erreur de syntaxe:

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

J'ai vérifié la documentation, et exécuter la sous-requête par lui-même, et tout semble vérifier. Quelqu'un peut-il comprendre ce qui ne va pas ici?

Update : Comme indiqué ci-dessous, MySQL ne permet pas d'utiliser la table supprimée dans une sous-requête pour la condition.

68
mikl

Vous ne pouvez pas spécifier de table cible pour la suppression.

Une solution de contournement

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);
32
ajreal

Pour les autres qui trouvent cette question cherchant à être supprimée lors de l'utilisation d'une sous-requête, je vous laisse cet exemple pour déjouer MySQL (même si certaines personnes semblent penser que cela ne peut pas être fait):

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

vous donnera une erreur:

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

Cependant cette requête:

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

fonctionnera très bien:

Query OK, 1 row affected (3.91 sec)

Enveloppez votre sous-requête dans une sous-requête supplémentaire (ici nommée x) et MySQL se fera un plaisir de faire ce que vous demandez.

225
CodeReaper

L'alias doit être inclus après le mot clé DELETE:

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
);
32
James Wiseman

Vous devez à nouveau faire référence à l'alias dans l'instruction delete, par exemple:

DELETE th FROM term_hierarchy AS th
....

Comme indiqué ici dans la documentation MySQL.

7
JNK

Je l'ai abordé d'une manière légèrement différente et cela a fonctionné pour moi;

Je devais supprimer secure_links de ma table qui faisait référence à la table conditions où il ne restait plus aucune ligne de condition. Un script de ménage essentiellement. Cela m'a donné l'erreur - Vous ne pouvez pas spécifier la table cible à supprimer.

En cherchant ici l’inspiration, j’ai trouvé la requête ci-dessous et cela fonctionne très bien ... C'est parce que cela crée une table temporaire sl1 qui est utilisée comme référence pour DELETE. 

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`
            )

Travaille pour moi.

6
Darren Edwards

La clause "in" dans la suppression ... n'est-elle pas extrêmement inefficace s'il y a un grand nombre de valeurs renvoyées par la sous-requête? Vous ne savez pas pourquoi vous ne voudriez pas simplement réintégrer à l'intérieur (ou à droite) la table d'origine de la sous-requête sur l'ID à supprimer, plutôt que de nous indiquer le "in (sous-requête)".?

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)

Et peut-être que la réponse est "MySQL ne le permet pas", cependant, cela fonctionne très bien pour moi. Supprimer avec rejoindre dans MySQL clarifie le problème DELETE/JOIN.

3
Jeff

Si vous voulez faire cela avec 2 requêtes, vous pouvez toujours faire quelque chose de similaire à ceci:

1) saisir les identifiants de la table avec:

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

Copiez ensuite le résultat avec la souris/le clavier ou le langage de programmation dans XXX ci-dessous:

2) DELETE FROM your_table WHERE id IN ( XXX )

Peut-être que vous pourriez le faire en une requête, mais c'est ce que je préfère.

1
TomoMiha

@CodeReaper, @BennyHill: Il fonctionne comme prévu. 

Cependant, je me demande quelle est la complexité temporelle pour avoir des millions de lignes dans la table? Apparemment, l’exécution de 5ms nécessitait environ 5 000 enregistrements sur une table correctement indexée. 

Ma question:

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?
0
rcadhikari