MySQL Error 1093 - Не удается указать целевую таблицу для обновления в предложении FROM - PullRequest
526 голосов
/ 05 сентября 2008

В моей базе данных есть таблица 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 ]

656 голосов
/ 05 сентября 2008

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

В 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 за этот совет в комментариях.

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

256 голосов
/ 23 марта 2012

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
)

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

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

102 голосов
/ 04 июня 2009

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);
89 голосов
/ 05 октября 2012

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

UPDATE skills AS s, (SELECT id  FROM skills WHERE type = 'Programming') AS p
SET s.type = 'Development' 
WHERE s.id = p.id;
33 голосов
/ 24 декабря 2011
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
)
21 голосов
/ 25 апреля 2017

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

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)

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

12 голосов
/ 02 сентября 2010

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

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

5 голосов
/ 05 сентября 2008

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

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

4 голосов
/ 08 января 2015

Согласно синтаксису ОБНОВЛЕНИЯ Mysql , связанному с @CheekySoft, написано прямо внизу.

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

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

2 голосов
/ 23 января 2018

попробуйте

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