Errore MySQL 1093: impossibile specificare la tabella di destinazione per l'aggiornamento nella clausola FROM
-
09-06-2019 - |
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?
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