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