MySQL错误1093-无法在FROM子句中指定目标表进行更新


591

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-您无法在FROM子句中指定目标表'story_category'进行更新

我该如何克服呢?



2
似乎MySQL错误跟踪器中的功能请求在此处:无法更新表并在子查询中从同一表中选择
Ben Creasy

Answers:


713

更新:此答案涵盖了常规错误分类。有关如何最好地处理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在评论中提供的建议。

示例技术来自Baron Schwartz,该书最初发表于Nabble在此释义并扩展。


1
支持此答案,因为我必须删除项目并且无法从另一个表获取信息,而不得不从同一表进行子查询。因为这是在搜索错误时弹出的顶部,所以我得到的这对我来说是最合适的答案,很多人试图在同一张表中查询时进行更新。
HMR 2012年

2
@Cheekysoft,为什么不将值保存到变量中呢?
Pacerier

19
请注意,从MySQL 5.7.6开始,优化程序可能会优化子查询并仍然给您错误,除非您SET optimizer_switch = 'derived_merge=off';:-(
Peter V.Mørch2015年

1
@PeterV.Mørch继mysqlserverteam.com/derived-tables-in-mysql-5-7,如果某些操作进行,合并是不可能发生的。例如,为派生的虚拟表提供一个LIMIT(到inifity),该错误将永远不会发生。不过,这是相当骇人听闻的,毕竟仍然存在将来的MySQL版本将支持将查询与LIMIT合并的风险。
user2180613 '16

您能否提供有关此解决方法的完整示例?更新tbl SET col =(SELECT ... FROM(SELECT .... FROM)AS x); 我仍然得到错误
JoelBonetR

310

NexusRex提供了一个很好的解决方案,用于从同一表中删除带有join的语句

如果您这样做:

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
)

它会做正确的事!

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


6
也许是因为我今天处于困境中,但这是最简单的答案,即使它可能不是“最佳”答案。
Tyler V.

4
这很好,谢谢!那么,这里的逻辑是什么?如果嵌套了一层,那么它将在外部之前执行?而且,如果它不是嵌套的,那么在删除表上有锁之后,mySQL会尝试运行它吗?
Flat Cat 2014年

43
这个错误和解决方案没有逻辑上的意义...但是可以。有时我想知道MySQL开发人员正在服用什么药物...
Cerin

1
同意@Cerin ..这是完全荒谬的,但是可以。
FastTrack

@ekonoval谢谢您的解决方案,但对我而言这没有什么意义,看起来您在愚弄MySQL并且他接受了,哈哈
deFreitas

106

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

5
这应该是最佳答案!也许删除第一个“代替”。
hoyhoy

1
我认为DISTINCT这里没有必要-为了获得更好的性能;)。
shA.t 2015年

1
我一定疯了。答案与原文中的答案相同。
杰夫·洛里

这也是我的答案。不要where in在ID列上,因此您不需要子查询主表。
理查德

@JeffLowery-第一个代码块是这里的答案;它是问题中的第二个代码块。
ToolmakerSteve

95

最近,我不得不更新同一表中的记录,如下所示:

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

11
这不可以写成UPDATE skills SET type='Development' WHERE type='Programming';吗?这似乎无法回答原始问题。
lilbyrdie 2014年

1
@lilbyrdie似乎有点杀伤力,它是正确的-只能是UPDATE skills SET type='Development' WHERE type='Programming';。我不明白为什么这么多人不考虑自己的工作...
shadyyx 2014年

1
这是最好的答案,恕我直言。它的语法很容易理解,您可以重复使用前面的语句,并且它不仅限于某些特殊情况。
斯特芬·温克勒

2
即使示例案例值得怀疑,该原理也是在实际场景中最好的理解和部署方法。所示查询之所以有效,是因为表列表中的隐式联接为子查询创建了一个临时表,从而提供了稳定的结果,并避免了在检索和修改同一表之间发生冲突。
AnrDaemon

1
不管有人怎么说这是过分的杀伤力,它仍然会在标题方面回答这个问题。在嵌套查询中使用同一表尝试更新时,也会遇到OP提到的错误
smac89,18年

35
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
您能解释一下为什么行得通吗,为什么只嵌套一个级别才能行?该问题已经作为对@EkoNoval问题的评论而被提出,但没有人回答。也许你可以帮忙。
Akshay Arora

@AkshayArora,在@Cheekysoft的答案中的“也许您可以将表自身连接到”标题下的部分进行浏览。他说UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col-这将用作此处使用的同一表的不同别名。类似地,在@NexusRex的答案中,第一个SELECT查询用作派生表story_category,第二次使用该表。因此,OP中提到的错误不应在此处发生,对吗?
Istiaque Ahmed

31

如果你做不到

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)

[更新或删除或其他]


所有以上答案的最可理解和合乎逻辑的实现。简单明了。
Clain Dsilva '19

现在,这已成为我工作流程的一部分。卡在这个错误上,转到这个答案并纠正查询。谢谢。
Vaibhav

13

这就是我在表中及其在WHERE子句中使用同一表上的子查询> 1时将Priority列值更新为1的方式,以确保至少一行包含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);

我知道这有点丑陋,但效果确实不错。


1
@anonymous_reviewer:如果给某人的评论提供[-1]甚至[+1],请同时说明您为什么给予它。谢谢!!!
sactiw 2010年

1
-1,因为这是不正确的。您不能修改SELECT语句中使用的同一表。
克里斯,

1
@Chris我已经在MySQL上对其进行了验证,它对我来说很好用,所以我请您在结束时对其进行验证,然后声明它是正确或不正确。谢谢!!!
sactiw 2011年

1
该页面底部的内容是:“当前,您无法更新表并在子查询中从同一表中选择。” -而且我在很多场合都经历过这种情况。 dev.mysql.com/doc/refman/5.0/en/update.html
克里斯(Chris)

19
@Chris我知道这一点,但是有一个解决方法,这正是我尝试通过“ UPDATE”查询显示的内容,并且相信我可以正常使用。我认为您根本没有尝试验证我的查询。
sactiw 2011年

6

最简单的方法是在子查询中引用父查询表时使用表别名。

范例:

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

5

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

这可能就是@Cheekysoft通过两步完成操作的意思。


3

根据@CheekySoft链接的Mysql UPDATE语法,它在底部显示。

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

我想您正在从store_category中删除,同时仍在联合中从中进行选择。


3

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

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

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

注意:DELETE s将删除操作限制在story_category表中。
文献资料

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

1
感到惊讶的是,它没有更多的赞成票。还应注意,多表语法也可用于UPDATE语句和联接的子查询。允许您执行LEFT JOIN ( SELECT ... )而不是WHERE IN( SELECT ... ),从而使实现在许多用例中都很有用。
fyrye

2

如果某事不起作用,则通过前门进入后门:

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
而且您刚刚丢失了所有外键,也许您也有一些级联的删除操作。
沃尔夫2014年



1

该查询如何希望对您有帮助

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

结果显示:'#1064-您的SQL语法有错误;检查与您的MariaDB服务器版本相对应的手册,以获取正确的语法,以在“ LEFT JOIN(SELECT category.id FROM类别)cat ON story_category.id = cat”附近使用。在1号线
伊斯蒂亚克·艾哈迈德

使用多表删除时,必须指定受影响的表。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
联接

0

就您而言,您要删除中story_category不存在的行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
);

NOT INJOIN原始表的子查询相结合似乎不必要地令人费解。这可以通过not exists和相关子查询以更直接的方式表示:

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

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

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

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

DB Fiddle上的演示

-- set-up
create 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 rows
SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);
| category_id |
| ----------:|
| 1 |
| 2 |
| 3 |
-- a functionally-equivalent, simpler query for this
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id)
| category_id |
| ----------:|
| 1 |
| 2 |
| 3 |
-- the delete query
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);

-- outcome
select * from story_category;
| category_id |
| ----------:|
| 4 |
| 5 |
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.