MySQL DELETE FROM com subconsulta como condição


87

Estou tentando fazer uma consulta como esta:

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

Como você provavelmente pode dizer, quero excluir a relação dos pais com 1015 se o mesmo tid tiver outros pais. No entanto, isso me resulta em um erro de sintaxe:

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

Eu verifiquei a documentação e executei a subconsulta por si só, e parece que tudo deu certo. Alguém consegue descobrir o que está errado aqui?

Atualização : conforme respondido abaixo, o MySQL não permite que a tabela da qual você está excluindo seja usada em uma subconsulta para a condição.


3
Atenção : Boa resposta na parte inferior stackoverflow.com/a/4471359/956397 simplesmente adicione o alias da tabela apósDELETE t FROM table t ...
— PiTheNumber

Respostas:


40

Você não pode especificar a tabela de destino para exclusão.

Uma solução alternativa

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

estamos ambos certos - veja seu comentário à minha resposta abaixo. A sintaxe e a lógica do alias eram os dois problemas :)
— JNK

Sim, parece que a exclusão via subconsulta não é possível no MySQL - obrigado por dar uma olhada nisso :)
— mikl

"DELETE FROM term_hierarchy AS th" na última linha não tem o mesmo problema? Recebo um erro de sintaxe igual ao OP.
— malhal

Você deve adicionar Index a term_hierarchy_backup.tid.
— Roman Newaza

1
Posso verificar que não é possível em 2019 no MariaDB 10.3.14 ou no MySQL Community Server 5.7.27
— alpham8

291

Para outros que encontram esta questão procurando excluir ao usar uma subconsulta, deixo este exemplo para superar o MySQL (mesmo que algumas pessoas pareçam pensar que isso não pode ser feito):

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

apresentará um erro:

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

No entanto, esta consulta:

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

vai funcionar bem:

Query OK, 1 row affected (3.91 sec)

Envolva sua subconsulta em uma subconsulta adicional (aqui denominada x) e o MySQL fará o que você pedir.


10
Demorou, mas comecei a trabalhar. Importante: 1) A primeira tabela deve ter o alias mostrado aqui com "e", 2) o "x" no final não é um espaço reservado, é o alias para a tabela temporária produzida pela subconsulta "(SELECT id FROM tableE ONDE arg = 1 AND foo = 'bar') ".
— Tilman Hausherr

3
Por que isso funciona? Isso muda muito para mim, mas, além disso, não deve funcionar. Ele faz o trabalho, mas não deveria.
— donatJ

1
inacreditável. isso realmente funciona! mas você não é forçado a usar o apelido da tabela e ... você pode usar qualquer apelido que desejar.
— Andrei Sandulescu

1
@jakabadambalazs Meu raciocínio quando surgiu com isso, foi que a subconsulta começando com "SELECT id" termina e retorna uma lista de ids e, portanto, libera o bloqueio da tabela que você deseja excluir.
— CodeReaper de

9
@jakabadambalazs: Não podemos usar a mesma tabela ( e) em um DELETE e em seu sub-SELECT. Nós podemos , no entanto usar uma sub-sub-SELECT para criar uma tabela temporária ( x) e uso que para a sub-SELECT.
— Steve Almond,

40

O alias deve ser incluído após a DELETEpalavra-chave:

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

3
Esta é uma boa resposta. O aliasing adequado irá percorrer um longo caminho para resolver problemas semelhantes aos da postagem original. (como o meu.)
— usumoio

11

Você precisa se referir ao alias novamente na instrução delete, como:

DELETE th FROM term_hierarchy AS th
....

Conforme descrito aqui na documentação do MySQL.


não se trata de alias, verifique o OP novamente
— ajreal

@ajreal - Sim, e observe que o erro começa na definição do alias e a documentação do MySQL afirma explicitamente que você precisa usar o alias na instrução DELETE, bem como na cláusula FROM. Obrigado pelo voto negativo, no entanto.
— JNK

simplesmente faça isso delete from your_table as t1 where t1.id in(select t2.id from your_table t2);o que você conseguiu?
— ajreal

4
A documentação afirma claramente; 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
você não precisa corrigir o alias, apenas não especifique a tabela de destino para selecionar na exclusão ... este é o verdadeiro problema
— ajreal

7

Abordei isso de uma maneira um pouco diferente e funcionou para mim;

Eu precisava remover secure_linksda minha tabela que fazia referência à conditionstabela onde não havia mais linhas de condição restantes. Um script de manutenção basicamente. Isso me deu o erro - Você não pode especificar a tabela de destino para exclusão.

Procurando aqui por inspiração, eu vim com a consulta abaixo e ela funciona muito bem. Isso ocorre porque ele cria uma tabela temporária sl1que é usada como referência para o 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`
            )

Funciona para mim.


Duplicado daquele do @CodeReaper acima ... boa chamada, tho ...;)
— jrypkahauer

5

A cláusula "in" na exclusão não é ... onde, extremamente ineficiente, se houver um grande número de valores retornados da subconsulta? Não tenho certeza por que você não faria apenas a junção interna (ou direita) de volta à tabela original da subconsulta no ID para excluir, em vez de usar o "in (subconsulta)".?

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)

E talvez seja respondido no "MySQL não permite", no entanto, está funcionando bem para mim, desde que eu tenha certeza de esclarecer completamente o que excluir (DELETE T FROM Target AS T). Excluir com Join no MySQL esclarece o problema DELETE / JOIN.


2

Se você quiser fazer isso com 2 consultas, você sempre pode fazer algo semelhante a isto:

1) pegar ids da mesa com:

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

Em seguida, copie o resultado com mouse / teclado ou linguagem de programação para XXX abaixo:

2) DELETE FROM your_table WHERE id IN ( XXX )

Talvez você pudesse fazer isso em uma consulta, mas é o que eu prefiro.


0

@CodeReaper, @BennyHill: Funciona conforme o esperado.

No entanto, eu me pergunto a complexidade de tempo para ter milhões de linhas na tabela? Aparentemente, demorou 5mspara ser executado por ter 5k registros em uma tabela indexada corretamente.

Minha consulta:

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

você pode usar o alias desta forma na instrução delete

DELETE  th.*
FROM term_hierarchy th
INNER JOIN term_hierarchy th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th.parent = 1015;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.