Errore MySQL 1093: impossibile specificare la tabella di destinazione per l'aggiornamento nella clausola FROM

StackOverflow https://stackoverflow.com/questions/45494

Domanda

Ho un tavolo story_category nel mio database con voci corrotte.La query successiva restituisce le voci corrotte:

SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);

Ho provato a cancellarli eseguendo:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category 
      INNER JOIN story_category ON category_id=category.id);

Ma ottengo il prossimo errore:

#1093 - Non è possibile specificare la tabella di destinazione 'story_category' per l'aggiornamento nella clausola FROM

Come posso superare questo problema?

È stato utile?

Soluzione

Aggiornamento:Questa risposta copre la classificazione generale degli errori.Per una risposta più specifica su come gestire al meglio la query esatta dell'OP, vedere altre risposte a questa domanda

In MySQL, non puoi modificare la stessa tabella che usi nella parte SELECT.
Questo comportamento è documentato in:http://dev.mysql.com/doc/refman/5.6/en/update.html

Forse puoi semplicemente unire il tavolo a se stesso

Se la logica è sufficientemente semplice da rimodellare la query, perdere la sottoquery e unire la tabella a se stessa, utilizzando criteri di selezione appropriati.Ciò farà sì che MySQL veda la tabella come due cose diverse, consentendo il proseguimento di modifiche distruttive.

UPDATE tbl AS a
INNER JOIN tbl AS b ON ....
SET a.col = b.col

In alternativa, prova a nidificare la sottoquery più in profondità in una clausola from...

Se hai assolutamente bisogno della sottoquery, c'è una soluzione alternativa, ma è brutta per diversi motivi, comprese le prestazioni:

UPDATE tbl SET col = (
  SELECT ... FROM (SELECT.... FROM) AS x);

La sottoquery nidificata nella clausola FROM crea un file Tabella temporanea implicita, quindi non conta come la stessa tabella che stai aggiornando.

...ma fai attenzione all'ottimizzatore di query

Tuttavia, fai attenzione a quello da MySQL 5.7.6 e in poi, l'ottimizzatore potrebbe ottimizzare la sottoquery e comunque darti l'errore.Fortunatamente, il optimizer_switch la variabile può essere utilizzata per disattivare questo comportamento;anche se non potrei raccomandare di farlo come qualcosa di più di una soluzione a breve termine o per piccole attività una tantum.

SET optimizer_switch = 'derived_merge=off';

Grazie a Pietro V.Morch per questo consiglio nei commenti.

La tecnica di esempio veniva dal Barone Schwartz, originariamente pubblicato su Nabble, qui parafrasato ed esteso.

Altri suggerimenti

NexusRex fornito a ottima soluzione per eliminare con join dalla stessa tabella.

Se lo fai:

DELETE FROM story_category
WHERE category_id NOT IN (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
)

riceverai un errore.

Ma se racchiudi la condizione in un'altra selezione:

DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
    ) AS c
)

sarebbe la cosa giusta!!

Spiegazione: L'ottimizzatore di query esegue a ottimizzazione della fusione derivata per la prima query (che ne causa il fallimento con l'errore), ma la seconda query non è idonea per il ottimizzazione della fusione derivata.Quindi l'ottimizzatore è costretto a eseguire prima la sottoquery.

IL inner join nella tua sottoquery non è necessario.Sembra che tu voglia eliminare le voci in story_category dove il category_id non è nel category tavolo.

Fai questo:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category);

Al posto di quello:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN
         story_category ON category_id=category.id);

Recentemente ho dovuto aggiornare i record nella stessa tabella, come ho fatto di seguito:

UPDATE skills AS s, (SELECT id  FROM skills WHERE type = 'Programming') AS p
SET s.type = 'Development' 
WHERE s.id = p.id;
DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category INNER JOIN story_category ON category_id=category.id
    ) AS c
)

Se non puoi farlo

UPDATE table SET a=value WHERE x IN
    (SELECT x FROM table WHERE condition);

poiché è la stessa tabella, puoi truccare e fare:

UPDATE table SET a=value WHERE x IN
    (SELECT * FROM (SELECT x FROM table WHERE condition) as t)

[aggiornare o eliminare o altro]

Questo è quello che ho fatto per aggiornare il valore di una colonna Priorità di 1 se è >=1 in una tabella e nella sua clausola WHERE utilizzando una sottoquery sulla stessa tabella per assicurarmi che almeno una riga contenga Priorità=1 (perché quella era la condizione da verificare durante l'esecuzione dell'aggiornamento):


UPDATE My_Table
SET Priority=Priority + 1
WHERE Priority >= 1
AND (SELECT TRUE FROM (SELECT * FROM My_Table WHERE Priority=1 LIMIT 1) as t);

Lo so, è un po' brutto, ma funziona bene.

È possibile inserire gli ID delle righe desiderate in una tabella temporanea e quindi eliminare tutte le righe trovate in quella tabella.

che potrebbe essere ciò che intendeva @Cheekysoft facendolo in due passaggi.

Secondo la sintassi Mysql UPDATE collegato da @CheekySoft, dice proprio in fondo.

Attualmente non è possibile aggiornare una tabella e selezionare dalla stessa tabella in una sottoquery.

Immagino che tu stia eliminando da store_category mentre continui a selezionarlo nell'unione.

Se qualcosa non funziona, quando passi dalla porta principale, prendi la porta sul retro:

drop table if exists apples;
create table if not exists apples(variety char(10) primary key, price int);

insert into apples values('fuji', 5), ('gala', 6);

drop table if exists apples_new;
create table if not exists apples_new like apples;
insert into apples_new select * from apples;

update apples_new
    set price = (select price from apples where variety = 'gala')
    where variety = 'fuji';
rename table apples to apples_orig;
rename table apples_new to apples;
drop table apples_orig;

È veloce.Più grandi sono i dati, meglio è.

Il modo più semplice per farlo è utilizzare un alias di tabella quando si fa riferimento alla tabella della query principale all'interno della sottoquery.

Esempio :

insert into xxx_tab (trans_id) values ((select max(trans_id)+1 from xxx_tab));

Cambialo in:

insert into xxx_tab (trans_id) values ((select max(P.trans_id)+1 from xxx_tab P));

prova questo

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc;

Prova a salvare il risultato dell'istruzione Select in una variabile separata e quindi utilizzala per eliminare la query.

che ne dici di questa query, spero che sia d'aiuto

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top