MySQL错误1093-无法指定目标表以进行更新的from子句

我的数据库中有一个包含损坏条目的表story_category。下一个查询返回损坏的条目:

SELECT *FROM  story_categoryWHERE category_id NOT IN (SELECT DISTINCT category.idFROM category INNER JOINstory_category ON category_id=category.id);

我试图删除它们执行:

DELETE FROM story_categoryWHERE category_id NOT IN (SELECT DISTINCT category.idFROM categoryINNER JOIN story_category ON category_id=category.id);

但我得到了下一个错误:

#1093-您不能指定目标表“story_category”以进行更新

我怎样才能克服这个?

664277 次浏览

更新:此答案涵盖了一般错误分类。有关如何最好地处理OP的确切查询的更具体答案,请参阅此问题的其他答案

在MySQL中,您不能修改在SELECT部分中使用的同一个表。
这种行为记录在:http://dev.mysql.com/doc/refman/5.6/en/update.html

也许你可以把桌子连起来

如果逻辑足够简单,可以重新塑造查询,则删除子查询并使用适当的选择标准将表连接到自身。这将导致MySQL将表视为两个不同的东西,从而允许进行破坏性更改。

UPDATE tbl AS aINNER JOIN tbl AS b ON ....SET a.col = b.col

或者,尝试将子查询更深地嵌套到from子句中…

如果您绝对需要子查询,有一个解决方法,但它是原因有很多,包括性能:

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

OF子句中的嵌套子查询创建一个隐式临时table,因此它不算作您正在更新的同一个表。

…但是要小心查询优化器

但是,请注意,从MySQL 5.7.6开始,优化器可能会优化子查询,但仍会给你错误。幸运的是,optimizer_switch变量可用于关闭此行为;尽管我不建议这样做,但只是短期修复,或用于小型一次性任务。

SET optimizer_switch = 'derived_merge=off';

感谢Peter V. Mørch在评论中给出的建议。

示例技术来自Baron Schwartz,最初发表于Nabble,在这里进行了解释和扩展。

您可以将所需行的id插入临时表中,然后删除该表中找到的所有行。

这可能就是@Cheekysoft所说的分两步完成。

如果某事不起作用,当通过前门时,然后采取后门:

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

它很快。数据越大越好。

子查询中的inner join是不必要的。看起来您想删除story_category中的条目,其中category_id不在category表中。

而不是:

DELETE FROM story_categoryWHERE category_id NOT IN (SELECT DISTINCT category.idFROM category INNER JOINstory_category ON category_id=category.id);

这样做:

DELETE FROM story_categoryWHERE category_id NOT IN (SELECT DISTINCT category.idFROM category);

这就是我在表中的优先级列值>=1及其WHERE子句中更新优先级列值1时所做的,使用同一表上的子查询来确保至少有一行包含优先级=1(因为这是执行更新时要检查的条件):

UPDATE My_TableSET Priority=Priority + 1WHERE Priority >= 1AND (SELECT TRUE FROM (SELECT * FROM My_Table WHERE Priority=1 LIMIT 1) as t);

我知道这有点难看,但它确实工作得很好。

DELETE FROM story_categoryWHERE 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)

NexusRex提供了一个很好的解决方案,用于从同一表中删除连接。

如果你这样做:

DELETE FROM story_categoryWHERE category_id NOT IN (SELECT DISTINCT category.id AS cid FROM categoryINNER JOIN story_category ON category_id=category.id)

你会得到一个错误。

但是,如果您将条件包装在另一个选择中:

DELETE FROM story_categoryWHERE category_id NOT IN (SELECT cid FROM (SELECT DISTINCT category.id AS cid FROM categoryINNER JOIN story_category ON category_id=category.id) AS c)

它会做正确的事!!

说明:查询优化器对第一个查询执行派生合并优化(这会导致它失败并出现错误),但第二个查询不符合派生合并优化的条件。因此,优化器被迫首先执行子查询。

最近我不得不在同一个表中更新记录,我是这样做的:

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

根据@CheekySoft链接的 MySQL UPDATE语法,它在底部说。

目前,您无法更新表并在子查询中从同一表中进行选择。

我猜你正在从store_category中删除,同时仍然在联盟中进行选择。

尝试将选择语句的结果保存在单独的变量中,然后将其用于删除查询。

如果你做不到

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)

[更新或删除或其他]

这个查询怎么样希望有帮助

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_categoryWHERE category_id NOT IN (SELECT DISTINCT category.idFROM (SELECT * FROM STORY_CATEGORY) sc;

对于OP试图实现的特定查询,理想且最有效的方法是根本不使用子查询。

以下是OP的两个查询的LEFT JOIN版本:

SELECT s.*FROM story_category sLEFT JOIN category cON c.id=s.category_idWHERE c.id IS NULL;

注意:DELETE s将删除操作限制为story_category表。
需求文档

DELETE sFROM story_category sLEFT JOIN category cON c.id=s.category_idWHERE c.id IS NULL;

就问题而言,您希望删除story_category中不存在于category中的行。

以下是用于标识要删除的行的原始查询:

SELECT *FROM  story_categoryWHERE category_id NOT IN (SELECT DISTINCT category.idFROM category INNER JOINstory_category ON category_id=category.id);

NOT IN与原始表JOIN的子查询结合起来似乎不必要地复杂。这可以用not exists和相关的子查询以更直接的方式表达:

select sc.*from story_category scwhere not exists (select 1 from category c where c.id = sc.category_id);

现在很容易将其转换为delete语句:

delete from story_categorywhere not exists (select 1 from category c where c.id = story_category.category_id);

这个quer可以在任何MySQL版本以及我知道的大多数其他数据库中运行。

演示在DB小提琴

-- set-upcreate table story_category(category_id int);create table category (id int);insert into story_category values (1), (2), (3), (4), (5);insert into category values (4), (5), (6), (7);
-- your original query to identify offending rowsSELECT *FROM  story_categoryWHERE category_id NOT IN (SELECT DISTINCT category.idFROM category INNER JOINstory_category ON category_id=category.id);
| category_id || ----------: ||           1 ||           2 ||           3 |
-- a functionally-equivalent, simpler query for thisselect sc.*from story_category scwhere not exists (select 1 from category c where c.id = sc.category_id)
| category_id || ----------: ||           1 ||           2 ||           3 |
-- the delete querydelete from story_categorywhere not exists (select 1 from category c where c.id = story_category.category_id);
-- outcomeselect * from story_category;
| category_id || ----------: ||           4 ||           5 |