MySQL DELETE FROM con subquery come condizione


87

Sto provando a fare una query come questa:

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

Come probabilmente puoi dire, voglio eliminare la relazione genitore a 1015 se lo stesso tid ha altri genitori. Tuttavia, questo mi produce un errore di sintassi:

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

Ho controllato la documentazione ed eseguito la sottoquery da solo, e sembra che tutto funzioni. Qualcuno riesce a capire cosa c'è che non va qui?

Aggiornamento : come spiegato di seguito, MySQL non consente di utilizzare la tabella che stai eliminando in una sottoquery per la condizione.


3
Attenzione : buona risposta in fondo stackoverflow.com/a/4471359/956397 aggiungi semplicemente l'alias della tabella dopoDELETE t FROM table t ...
PiTheNumber

Risposte:


40

Non è possibile specificare la tabella di destinazione per l'eliminazione.

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

abbiamo entrambi ragione - vedi il suo commento alla mia risposta qui sotto. La sintassi e la logica alias erano entrambi problemi :)
JNK

Sì, sembra che l'eliminazione tramite subquery non sia attualmente possibile in MySQL - grazie per
averci dato

"DELETE FROM term_hierarchy AS th" nell'ultima riga non ha lo stesso problema? Ottengo un errore di sintassi uguale all'OP.
malhal

Devi aggiungere Index a term_hierarchy_backup.tid.
Roman Newaza

1
Sono in grado di verificare che non sia possibile nel 2019 su MariaDB 10.3.14 o MySQL Community Server 5.7.27
alpham8

292

Per altri che trovano questa domanda cercando di eliminare durante l'utilizzo di una sottoquery, vi lascio questo esempio per superare in astuzia MySQL (anche se alcune persone sembrano pensare che non possa essere fatto):

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

ti darà un errore:

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

Tuttavia questa domanda:

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

funzionerà perfettamente:

Query OK, 1 row affected (3.91 sec)

Avvolgi la tua sottoquery in un'altra sottoquery (qui chiamata x) e MySQL farà felicemente quello che chiedi.


10
Ci è voluto un po 'di tempo ma l'ho fatto funzionare. Importante: 1) La prima tabella deve essere alias come mostrato qui con "e", 2) la "x" alla fine non è un segnaposto, è l'alias per la tabella temporanea prodotta dalla sottoquery "(SELECT id FROM tableE WHERE arg = 1 AND foo = 'bar') ".
Tilman Hausherr

3
Perché funziona? Questo cambia molto per me, ma inoltre non dovrebbe funzionare. Si fa il lavoro, ma non dovrebbe.
donatJ

1
incredibile. funziona davvero! ma non sei obbligato ad alias la tabella con e ... puoi usare qualsiasi alias tu voglia.
Andrei Sandulescu

1
@jakabadambalazs Il mio ragionamento quando l'ho inventato è stato che la sottoquery che inizia con "SELECT id" termina e restituisce un elenco di ID e quindi rilascia il blocco della tabella da cui si desidera eliminare.
CodeReaper

9
@jakabadambalazs: non possiamo usare la stessa tabella ( e) in un DELETE e nella sua sub-SELECT. Noi possiamo però utilizzare un sub-sub-SELECT per creare una tabella temporanea ( x), e l'uso che per il sub-SELECT.
Steve Almond

40

L'alias dovrebbe essere incluso dopo la DELETEparola chiave:

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
Questa è una buona risposta. Il corretto aliasing farà molto per risolvere problemi simili al post originale. (come il mio)
usumoio

11

È necessario fare nuovamente riferimento all'alias nell'istruzione di eliminazione, ad esempio:

DELETE th FROM term_hierarchy AS th
....

Come descritto qui nei documenti MySQL.


non si tratta di alias, si prega di controllare di nuovo l'OP
ajreal

@ajreal - L'ho fatto, e per favore nota che l'errore inizia con la definizione dell'alias e la documentazione di MySQL afferma esplicitamente che devi usare l'alias nell'istruzione DELETE e nella clausola FROM. Grazie per il voto negativo, però.
JNK

fai semplicemente questo delete from your_table as t1 where t1.id in(select t2.id from your_table t2);cosa hai ottenuto?
ajreal

4
La documentazione afferma chiaramente; 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
non è necessario correggere l'alias, semplicemente non specificare la tabella di destinazione per la selezione in eliminazione ... questo è il vero problema
ajreal

7

Mi sono avvicinato a questo in un modo leggermente diverso e ha funzionato per me;

Avevo bisogno di rimuovere secure_linksdalla mia tabella che conditionsfaceva riferimento alla tabella in cui non c'erano più righe di condizioni. Fondamentalmente un copione per le pulizie. Questo mi ha dato l'errore: non è possibile specificare la tabella di destinazione per l'eliminazione.

Quindi, cercando ispirazione qui, mi è venuta in mente la query seguente e funziona perfettamente. Questo perché crea una tabella temporanea sl1che viene utilizzata come riferimento per 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`
            )

Per me va bene.


Duplicato di quello di @CodeReaper sopra ... buona chiamata però ...;)
jrypkahauer

5

La clausola "in" nella cancellazione ... dove, estremamente inefficiente, se ci sarà un gran numero di valori restituiti dalla sottoquery? Non sei sicuro del motivo per cui non dovresti semplicemente ricongiungerti alla tabella originale dalla sottoquery sull'ID da eliminare, piuttosto che utilizzare "in (sottoquery)".?

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 forse è risposto "MySQL non lo consente", tuttavia, funziona bene per me FORNITO Mi assicuro di chiarire completamente cosa eliminare (DELETE T FROM Target AS T). Elimina con Join in MySQL chiarisce il problema DELETE / JOIN.


2

Se vuoi farlo con 2 query, puoi sempre fare qualcosa di simile a questo:

1) prendi gli ID dalla tabella con:

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

Quindi copia il risultato con mouse / tastiera o linguaggio di programmazione su XXX di seguito:

2) DELETE FROM your_table WHERE id IN ( XXX )

Forse potresti farlo in una query, ma questo è quello che preferisco.


0

@CodeReaper, @BennyHill: funziona come previsto.

Tuttavia, mi chiedo la complessità temporale per avere milioni di righe nella tabella? Apparentemente, ci sono voluti circa 5msper eseguire per avere 5k record su una tabella correttamente indicizzata.

La mia domanda:

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

puoi usare l'alias in questo modo sull'istruzione 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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.