Ошибка 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

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

Ответ 1

Обновление: этот ответ охватывает общую классификацию ошибок. Для более конкретного ответа о том, как лучше всего обрабатывать точный запрос 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

В качестве альтернативы, попробуйте вложить подзапрос глубже в предложение 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, перефразированный и расширенный здесь.

Ответ 2

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

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

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
)

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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
)

Ответ 6

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

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)

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

Ответ 7

Это то, что я сделал для обновления значения столбца 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);

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

Ответ 8

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

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

Ответ 9

В соответствии с синтаксисом Mysql UPDATE, связанным с @CheekySoft, он говорит прямо внизу.

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

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

Ответ 10

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

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;

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

Ответ 11

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

Пример:

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

Ответ 12

попробуйте это

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

Ответ 13

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

Ответ 14

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

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

Ответ 15

Для конкретного запроса, который пытается выполнить OP, идеальный и наиболее эффективный способ сделать это - НЕ использовать подзапрос вообще. Вот версии LEFT JOIN OP двух запросов:

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

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