У меня есть таблица 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
Как я могу преодолеть это?
mysql
subquery
sql-delete
mysql-error-1093
Серхио дель Амо
источник
источник
Ответы:
Обновление: этот ответ охватывает общую классификацию ошибок. Чтобы получить более конкретный ответ о том, как лучше всего обрабатывать точный запрос ОП, см. Другие ответы на этот вопрос.
В MySQL вы не можете изменить ту же таблицу, которую используете в части SELECT.
Это поведение описано на странице : http://dev.mysql.com/doc/refman/5.6/en/update.html.
Может быть, вы можете просто присоединиться к столу к себе
Если логика достаточно проста, чтобы изменить запрос, потеряйте подзапрос и присоедините таблицу к себе, используя соответствующие критерии выбора. Это приведет к тому, что MySQL увидит таблицу как две разные вещи, позволяющие вносить деструктивные изменения.
В качестве альтернативы попробуйте вложить подзапрос глубже в предложение from ...
Если вам абсолютно необходим подзапрос, есть обходной путь, но он уродлив по нескольким причинам, включая производительность:
Вложенный подзапрос в предложении FROM создает неявную временную таблицу , поэтому он не считается той же самой таблицей, которую вы обновляете.
... но следите за оптимизатором запросов
Тем не менее, имейте в виду , что начиная с MySQL 5.7.6 и далее, оптимизатор может оптимизировать подзапрос и все равно выдавать ошибку. К счастью,
optimizer_switch
переменная может использоваться, чтобы отключить это поведение; хотя я не мог рекомендовать делать это как что-то большее, чем краткосрочное исправление или для небольших одноразовых задач.Спасибо Peter V. Mørch за этот совет в комментариях.
Пример техники был от барона Шварца, первоначально опубликованный в Набле , перефразированный и расширенный здесь.
источник
SET optimizer_switch = 'derived_merge=off';
:-(NexusRex предоставил очень хорошее решение для удаления с помощью объединения из той же таблицы.
Если вы делаете это:
вы получите ошибку.
Но если вы заключите условие в еще одно, выберите:
это сделало бы правильную вещь !!
Объяснение: Оптимизатор запросов выполняет производную оптимизацию слияния для первого запроса (что приводит к сбою при ошибке), но второй запрос не подходит для производной оптимизации слияния . Следовательно, оптимизатор сначала должен выполнить подзапрос.
источник
В
inner join
вашем подзапросе нет необходимости. Похоже, вы хотите удалить записи,story_category
гдеcategory_id
нет вcategory
таблице.Сделай это:
Вместо этого:
источник
DISTINCT
здесь нет необходимости - для лучшей производительности;).where in
в столбце ID, поэтому вам не нужно запрашивать первичную таблицу.Недавно мне пришлось обновить записи в той же таблице, я сделал это, как показано ниже:
источник
UPDATE skills SET type='Development' WHERE type='Programming';
? Это, кажется, не отвечает на оригинальный вопрос.UPDATE skills SET type='Development' WHERE type='Programming';
. Я не понимаю, почему так много людей не думают о том, что они делают ...источник
UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col
- это будет работать, так как здесь используется другой псевдоним для той же таблицы. Аналогично в ответе @ NexusRex первыйSELECT
запрос действует как производная таблица, в которуюstory_category
используется второй раз. Таким образом, ошибка, упомянутая в ОП, не должна иметь место здесь, верно?Если вы не можете сделать
потому что это та же таблица, вы можете обмануть и сделать:
[обновить или удалить или что-то еще]
источник
Это то, что я сделал для обновления значения столбца Приоритет на 1, если оно равно> = 1 в таблице и в его предложении WHERE, используя подзапрос к той же таблице, чтобы убедиться, что хотя бы одна строка содержит Приоритет = 1 (потому что это был условие, которое проверяется при выполнении обновления):
Я знаю, что это немного некрасиво, но работает нормально.
источник
Самый простой способ сделать это - использовать псевдоним таблицы, когда вы ссылаетесь на родительскую таблицу запросов внутри подзапроса.
Пример :
Измените это на:
источник
Вы можете вставить идентификаторы нужных строк во временную таблицу, а затем удалить все строки, найденные в этой таблице.
что может быть то, что @Cheekysoft имел в виду, делая это в два шага.
источник
Согласно синтаксису ОБНОВЛЕНИЯ Mysql, связанному с @CheekySoft, он написан прямо внизу.
Я предполагаю, что вы удаляете из store_category, все еще выбирая его в объединении.
источник
Для конкретного запроса, который пытается выполнить OP, идеальный и наиболее эффективный способ сделать это - НЕ использовать подзапрос вообще.
Вот
LEFT JOIN
версии двух запросов ОП:Примечание:
DELETE s
ограничивает операции удаленияstory_category
таблицей.Документация
источник
UPDATE
операторами и присоединенными подзапросами. Позволяя вам выполнятьLEFT JOIN ( SELECT ... )
в противоположностьWHERE IN( SELECT ... )
, делая реализацию полезной во многих случаях использования.Если что-то не работает, когда вы проходите через парадную дверь, возьмите заднюю дверь:
Это быстро. Чем больше данных, тем лучше.
источник
Попробуйте сохранить результат оператора Select в отдельной переменной, а затем использовать его для запроса на удаление.
источник
попробуй это
источник
как насчет этого запроса надеюсь, что это поможет
источник
DELETE story_category FROM ...
однако присоединенный подзапрос не является обязательным в этом контексте и может быть выполнен с использованием.LEFT JOIN category AS cat ON cat.id = story_category.category_id WHERE cat.id IS NULL
Обратите внимание, что критерии ответа в ответе неверно ссылаютсяstory_category.id = cat.id
Что касается, вы хотите удалить строки,
story_category
которые не существуют вcategory
.Вот ваш оригинальный запрос для определения удаляемых строк:
В сочетании
NOT IN
с подзапросомJOIN
исходная таблица выглядит излишне запутанной. Это может быть выражено более простым способом сnot exists
помощью коррелированного подзапроса:Теперь легко превратить это в
delete
утверждение:Этот запрос будет работать на любой версии MySQL, а также на большинстве других баз данных, которые я знаю.
Демо на БД Fiddle :
источник