Błąd MySQL 1093 - nie można określić tabeli docelowej dla aktualizacji w klauzuli FROM

Mam tabelę story_category w mojej bazie danych z uszkodzonymi wpisami. Następne zapytanie zwraca uszkodzone wpisy:

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

Próbowałem je usunąć:

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

Ale dostaję następny błąd:

#1093 - nie można określić docelowej tabeli 'story_category' dla aktualizacji w klauzuli FROM

Jak mogę to przezwyciężyć?
Author: shA.t, 2008-09-05

16 answers

Aktualizacja: ta odpowiedź obejmuje ogólną klasyfikację błędów. Aby uzyskać bardziej szczegółową odpowiedź na temat tego, jak najlepiej poradzić sobie z dokładnym zapytaniem OP, zobacz inne odpowiedzi na to pytanie

W MySQL nie można modyfikować tej samej tabeli, której używasz w części SELECT.
Zachowanie to udokumentowane jest na: http://dev.mysql.com/doc/refman/5.6/en/update.html

Może możesz po prostu dołączyć stół do siebie

Jeśli logika jest na tyle prosta, aby zmień kształt zapytania, utrać zapytanie podrzędne i dołącz tabelę do siebie, stosując odpowiednie kryteria wyboru. Spowoduje to, że MySQL będzie postrzegał tabelę jako dwie różne rzeczy, co pozwoli na wprowadzenie destrukcyjnych zmian.

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

Alternatywnie, spróbuj zagnieżdżać zapytanie podrzędne głębiej w klauzuli from ...

Jeśli absolutnie potrzebujesz subquery, jest obejście, ale to brzydka z kilku powodów, w tym wydajność:

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

Zagnieżdżone zapytanie podrzędne w FROM klauzula tworzy implicit temporary Tabela , więc nie liczy się jako ta sama tabela, którą aktualizujesz.

... ale uważaj na optymalizator zapytań

Uważaj jednak, że od MySQL 5.7.6 i dalej, optymalizator może zoptymalizować zapytanie podrzędne i nadal dać ci błąd. Na szczęście zmienna optimizer_switch może być użyta do wyłączenia tego zachowania; chociaż nie polecam tego zrobić jako coś więcej niż krótkoterminowa poprawka lub małe jednorazowe zadania.

SET optimizer_switch = 'derived_merge=off';

dzięki Peter V. Mørch za tę radę w komentarzach.

Przykładowa technika pochodzi od barona Schwartza, pierwotnie opublikowana w Nabble [11]}, sparafrazowana i rozszerzona tutaj.
 765
Author: Cheekysoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:45

NexusRex dostarczył bardzo dobre rozwiązanie do usuwania za pomocą join z tej samej tabeli.

Jeśli to zrobisz:

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
)

Dostaniesz błąd.

Ale jeśli zawijasz warunek w jeszcze jeden Wybierz:

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
)
To by dobrze zrobiło!!

Explanation: optymalizator zapytań wykonuje pochodna optymalizacja łączenia dla pierwszego zapytania (co powoduje, że nie powiodło się z błędem), ale drugie zapytanie nie kwalifikuje się do pochodnej optymalizacji merge . Dlatego optymalizator jest zmuszony najpierw wykonać zapytanie podrzędne.

 354
Author: Yehor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-09 16:10:32

inner join w zapytaniu podrzędnym jest niepotrzebne. Wygląda na to, że chcesz usunąć wpisy z story_category, Gdzie category_id nie znajduje się w tabeli category.

Zrób to:

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

Zamiast tego:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN
         story_category ON category_id=category.id);
 110
Author: shA.t,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-31 09:37:53

Ostatnio musiałem zaktualizować rekordy w tej samej tabeli zrobiłem to jak poniżej:

UPDATE skills AS s, (SELECT id  FROM skills WHERE type = 'Programming') AS p
SET s.type = 'Development' 
WHERE s.id = p.id;
 101
Author: Pratik Khadloya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-20 10:48:20

If you can ' t do

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

Ponieważ jest to ta sama tabela, możesz trick and do:

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

[update or delete or whatever]

 43
Author: Sequoya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-25 11:59:56
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
)
 37
Author: NexusRex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-23 20:03:08

To jest to, co zrobiłem, aby zaktualizować wartość kolumny Priorytetu o 1, jeśli jest >=1 w tabeli i w jej klauzuli WHERE, używając zapytania podrzędnego w tej samej tabeli, aby upewnić się, że co najmniej jeden wiersz zawiera Priorytet=1 (ponieważ był to warunek do sprawdzenia podczas aktualizacji):


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);
Wiem, że jest trochę brzydki, ale działa dobrze.
 14
Author: sactiw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 07:31:45

Najprostszym sposobem na to jest użycie aliasu tabeli, gdy odwołujesz się do nadrzędnej tabeli zapytań wewnątrz podrzędnego zapytania.

Przykład:

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

Zmień na:

insert into xxx_tab (trans_id) values ((select max(P.trans_id)+1 from xxx_tab P));
 7
Author: S.Roshanth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-11 02:14:39

Dla konkretnego zapytania, które OP próbuje osiągnąć, idealnym i najbardziej efektywnym sposobem na to jest nieużywanie w ogóle zapytań podrzędnych.

Oto LEFT JOIN wersje dwóch zapytań OP:

SELECT s.* 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

Uwaga: DELETE s ogranicza operacje usuwania do Tabeli story_category.
dokumentacja

DELETE s 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;
 7
Author: Doin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-09 19:05:15

Można wstawić ID żądanych wierszy do tabeli tymczasowej, a następnie usunąć wszystkie wiersze, które znajdują się w tej tabeli.

Co @Cheekysoft miał na myśli, robiąc to w dwóch krokach.

 5
Author: YonahW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-05 10:25:35

Zgodnie z składnią aktualizacji Mysql podlinkowaną przez @ CheekySoft, jest napisane na dole.

Obecnie nie można zaktualizować tabeli i wybrać z tej samej tabeli w zapytaniu podrzędnym.

Domyślam się, że usuwasz z store_category, jednocześnie wybierając z niego w Unii.

 3
Author: briankip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-08 13:18:46

Jeśli coś nie działa, przy wchodzeniu przez drzwi wejściowe, weź tylne drzwi:

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;
Jest szybki. Im większe DANE, tym lepiej.
 2
Author: Tom Schaefer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-29 02:26:30

Spróbuj zapisać wynik instrukcji Select w osobnej zmiennej, a następnie użyj jej do zapytania delete.

 2
Author: lipika chakraborty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-31 11:35:18

Spróbuj tego

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc;
 2
Author: Sameer Khanal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 01:14:45

A co z tym zapytaniem mam nadzieję, że pomoże

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL
 1
Author: Melvin Angelo Jabonillo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-11 01:55:03

Jeśli chodzi o problem, chcesz usunąć wiersze w story_category, które nie istnieją w category.

Oto oryginalne zapytanie, aby zidentyfikować wiersze do usunięcia:

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

Łączenie NOT IN z zapytaniem podrzędnym, które JOINwydaje się niepotrzebnie zawiłe. Można to wyrazić w bardziej prosty sposób za pomocą not exists i skorelowanej podquery:

select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id);

Teraz łatwo przekształcić to w delete stwierdzenie:

delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);    

To zapytanie uruchomi się na dowolnym MySQL wersji, jak również w większości innych baz danych, które znam.

Demo na DB Fiddle:

-- set-up
create table story_category(category_id int);
create table category (id int);
insert into story_category values (1), (2), (3), (4), (5);
insert into category values (4), (5), (6), (7);

-- your original query to identify offending rows
SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);
| category_id |
| ----------: |
|           1 |
|           2 |
|           3 |
-- a functionally-equivalent, simpler query for this
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id)
| category_id |
| ----------: |
|           1 |
|           2 |
|           3 |
-- the delete query
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);

-- outcome
select * from story_category;
| category_id |
| ----------: |
|           4 |
|           5 |
 0
Author: GMB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-10 10:46:12