2008-09-05 9 views
444

У меня есть таблица story_category в моей базе данных с поврежденными записями. Следующий запрос возвращает испорченные данные:Ошибка MySQL 1093 - Невозможно указать целевую таблицу для обновления в предложении FROM

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

Я пытался удалить их выполнения:

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

Но я получаю следующую ошибку:

#1093 - You can't specify target table 'story_category' for update in FROM clause

Как я могу преодолеть это?

+2

Связанный: http://stackoverflow.com/a/14302701/238419 – 2013-02-22 18:10:06

+0

Похоже запрос функции в отслеживателе ошибок MySQL находится здесь: [не может обновить таблицу и выбрать из той же таблицы в подзапросе] (https: //bugs.mys ql.com/bug.php?id=23353) – 2017-12-30 06:32:26

ответ

578

Обновление: Этот ответ охватывает общую классификацию ошибок. Более конкретный ответ о том, как лучше всего обрабатывать точный запрос OP, см. В других ответах на этот вопрос.

В MySQL вы не можете изменить ту же таблицу, которую используете в части SELECT.
Такое поведение документированы: http://dev.mysql.com/doc/refman/5.6/en/update.html

Может быть, вы можете просто присоединиться к таблице для себя

Если логика достаточно, чтобы повторно форму запроса, теряют вложенный запрос и присоединиться к таблице, чтобы просто используя соответствующие критерии отбора. Это заставит MySQL видеть таблицу как две разные вещи, что позволяет разрушительным изменениям идти вперед.

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

Кроме того, попробуйте вложенность подзапроса глубже в с п ...

Если вы абсолютно необходим подзапросом, есть обходной путь, но это уродливого по нескольким причинам, в том числе производительности:

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

вложенный подзапрос в ЕКЕ создает неявный временный таблицу, поэтому он не считается той же таблицей, которую вы обновляете.

... но следить за оптимизатором запросов

Однако нужно учитывать, что от MySQL 5.7.6 и вперед, оптимизатор может оптимизировать из подзапроса, и еще дать вам ошибку. К счастью, переменная optimizer_switch может использоваться для отключения этого поведения; хотя я не мог рекомендовать делать это как нечто большее, чем краткосрочное исправление, или для небольших одноразовых задач.

SET optimizer_switch = 'derived_merge=off'; 

Благодаря Peter V. Mørch для этого совета в комментариях.

Пример техники от Барона Шварца, originally published at Nabble, перефразированный и расширенный здесь.

+1

Упрощенный ответ, потому что мне пришлось удалять элементы и не удалось получить информацию из другой таблицы, пришлось подзапросить из той же таблицы. Так как это то, что всплывает наверху при поиске в Google для ошибки, я понял, что это лучший ответ для меня и многих людей, пытающихся обновить, в то время как subquerieing из той же таблицы. – HMR 2012-12-12 03:36:56

+2

@Cheekysoft, Почему бы не сохранить значения в переменных вместо этого? – Pacerier 2015-02-24 03:47:27

+15

Остерегайтесь, что из [MySQL 5.7.6 on] (http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-6.html) оптимизатор может оптимизировать подпункт -получите запрос и по-прежнему выдавайте ошибку, если только вы не наберете `SET optimizer_switch = 'производный_merge = off';` :-( – 2015-11-06 15:36:41

4

Вы можете вставить идентификаторы нужных строк в временную таблицу, а затем удалить все строки, найденные в этой таблице.

который может быть тем, что @Cheekysoft подразумевал, делая это за два шага.

1

Если что-то не работает, когда идет через парадную дверь, а затем взять обратно-дверь:

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; 

Это быстро. Чем больше данных, тем лучше.

+7

И вы только что потеряли все свои внешние ключи, и, возможно, у вас также есть каскадные удаления. – Walf 2014-12-09 06:48:49

98

inner join в вашем подзапросе нет необходимости. Похоже, вы хотите удалить записи в story_category, где category_id нет в таблице category.

ли это:

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

Вместо этого:

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

Это то, что я сделал для обновления значения столбца Priority на 1, если она> = 1 в таблице и в его WHERE с использованием подзапроса в той же таблице, чтобы удостовериться, что по крайней мере одна строка содержит Priority = 1 (поскольку это условие было проверено при выполнении обновления):


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

Я знаю, что это немного уродливо, но все работает отлично.

28
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 
) 
192

NexusRex предоставил very good solution для удаления с присоединиться из той же таблицы.

Если вы сделаете это:

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 
) 

Вы собираетесь получить ошибку.

Но если вы заключаете состояние еще в одном выберите

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 
) 

это будет делать правильные вещи !!

Объяснение: Оптимизатор делает derived merge optimization для первого запроса (который вызывает его на неудачу с ошибкой), но второй запрос не отвечает требованиям для производной оптимизации слияния, следовательно, оптимизатор вынужден сначала выполнить подзапрос.

77

Недавно я должен был обновить записи в одной таблице я сделал это, как показано ниже:

UPDATE skills AS s, (SELECT id FROM skills WHERE type = 'Programming') AS p 
SET s.type = 'Development' 
WHERE s.id = p.id; 
3

По the Mysql UPDATE Syntax связаны @CheekySoft, он говорит, что на самом дне.

Currently, you cannot update a table and select from the same table in a subquery.

Я думаю, вы удаляете из store_category, все еще выбирая из него в союзе.

1

Попробуйте сохранить результат оператора Select в отдельной переменной, а затем используйте это для запроса на удаление.

10

Если вы не можете сделать

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

, потому что это та же таблица, вы можете обмануть и сделать:

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

[обновление или удаление или любой другой]

0

как о этот запрос надеется, что это поможет

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL 
0

Самый простой способ сделать это - использовать псевдоним таблицы, когда вы ссылаетесь на родительскую таблицу запросов внутри подзапроса.

Пример:

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

Изменить это:

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

попробовать эту

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