Ошибка MySQL 1093 - не удается указать целевую таблицу для обновления в предложении FROM


у меня есть таблица story_category в моей базе данных с поврежденных записей. Следующий запрос возвращает поврежденные записи:

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-вы не можете указать целевую таблицу "story_category" для обновления в предложении FROM

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

14 471

14 ответов:

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

в MySQL вы не можете изменить ту же таблицу, которую вы используете в выбранной части.
Это поведение задокументировано по адресу: 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

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

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

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

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

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

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

SET optimizer_switch = 'derived_merge=off';

спасибо Peter V. Mørch за этот совет в комментариях.

пример техники был от Барона Шварца,первоначально опубликовано в Nabble, перефразировано и расширено здесь.

NexusRex предоставил очень хорошее решение для удаления с помощью join из той же таблицы.

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

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
)

вы получите сообщение об ошибке.

но если вы обернете условие в еще один select:

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
)

это было бы правильно!!

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

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

недавно мне пришлось обновить записи в той же таблице, что и ниже:

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
)

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

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)

[обновить или удалить или что-то еще]

Это то, что я сделал для обновления значения столбца приоритета на 1, если это >=1 в таблице и в ее предложении WHERE, используя подзапрос в той же таблице, чтобы убедиться, что хотя бы одна строка содержит приоритет=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);

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

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

Что может быть то, что @Cheekysoft имел в виду, делая это в два этапа.

по данным синтаксис обновления Mysql связанный @CheekySoft, он говорит прямо внизу.

в настоящее время, вы не можете обновить таблицу и выбрать из той же таблицы в подзапросе.

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

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

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;

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

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

Как насчет этого запроса надеюсь, что это поможет

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

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

пример :

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

попробуй такое

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