MySQL #1093 - 无法在FROM子句中指定目标表'giveaways'进行更新

26

我尝试了:

UPDATE giveaways SET winner = '1' WHERE ID = (SELECT MAX(ID) FROM giveaways)

但是它会给出以下错误信息:

#1093 - 无法在FROM子句中指定目标表 'giveaways' 进行更新

这篇文章看起来很相关,但我无法将其适应到我的查询中。我该如何让它工作?


1
您正在进行递归查询。您想要做什么? - Gigi
1
giveaways表中只有少量记录。我想将拥有最大ID的赠品的获胜者列设置为1。 - Eray
1
@Gigi,很明显他想做什么。*UPDATE giveaways SET winner = '1' WHERE ID = (SELECT MAX(ID) FROM giveaways)* - Pacerier
6个回答

38
根据您提供的文章信息,以下内容应该有效:

基于您提供的文章信息,以下内容应该有效:

update giveaways set winner='1'
where Id = (select Id from (select max(Id) as id from giveaways) as t)

1
谢谢,它正在工作!但是你能解释一下,我应该选择哪种方式来提高性能吗?你的方式还是@nick rulez的方式(https://dev59.com/gWsy5IYBdhLWcg3wxAyO#8333417)? - Eray
2
@Eray。我认为我的查询速度不会比ipr101慢,因为即使我使用“*”,查询也只检索一个标量值。所以我不会得到任何不必要的值。顺便说一句,我已经投票支持Matthew的答案,因为它是更聪明的解决方案,我不明白为什么有人会对它进行反对票。 - Nicola Cossu
1
@Pacerier 把它保存到一个变量中,然后使用该变量将不再是巧合编码,而是引入了竞争条件。 - Matthew
@Matthew,当然我们会用start transactioncommit来包围这些语句。这样就不会出现竞争条件了。 - Pacerier
1
@Pacerier,你不会得到死锁,但会出现竞态条件。如果你不想要竞态条件,你需要一个表锁。这并非固有的死锁问题。 - Matthew
显示剩余5条评论

30
这是因为你的更新可能是循环的...如果更新该记录导致使WHERE条件变为FALSE,会发生什么? 你知道这不是真的,但引擎不知道。操作表中也可能存在相反的锁定。
我认为可以这样做(未经测试):
UPDATE
    giveaways
SET
    winner = '1'
ORDER BY
    id DESC
LIMIT 1

阅读更多


2
经过测试和批准。它只需0.0002秒。 - Eray
@Matthew,关于“如果更新该记录会导致使WHERE条件为FALSE的情况发生”的问题,既然where子句已经被评估过了,那为什么这会有影响呢?为什么MySQL不能自动地做到这一点(ipr101和nick的回答)? - Pacerier
1
@Pacerier,原因是这些解决方案使用了针对嵌套子查询的隐式查询。当您告诉引擎从嵌套子查询中选择内容时,您会隐式地强制执行该语句。如果不这样做(如所写),查询规划器将尝试优化嵌套。这是“巧合编码”,这意味着您依赖于查询规划器的特定行为,这是不良实践。 - Matthew
1
@Matthew,那么,真正的问题不是与“查询规划器将尝试优化嵌套”有关吗?在像update t where id=(select max(id)from t)这样的查询中,很明显查询规划器不应该尝试优化嵌套,因为可以看出(select max(id)from t)是应该首先运行的内部查询。 - Pacerier
@Pacerier 不是的。查询规划器的默认行为通常不会首先执行嵌套子查询,因为它们可能能够更有效地组合在外部查询中。两个答案都利用了这个显式的SELECT,强制查询规划器的行为。当然,这样做是可以的,但是这是在反对规划器。这是巧合编码。您无法控制查询规划器的行为,也不应该编写代码以利用其巧合的行为。 - Matthew
@Matthew,这才是真正的问题。查询规划器在尽可能优化脚本时存在。由于它足够聪明,知道update t where id=(select max(id)from t)有一个循环,因此对于这些情况,它很容易告诉自己首先执行嵌套子查询。对于未检测到循环的情况,只要结果是引用透明的,它就可以自由地以任何方式组合查询。 - Pacerier

13
update giveaways set winner=1 
where Id = (select*from (select max(Id)from giveaways)as t)

谢谢,它正在工作!但是您能解释一下,我应该选择哪种方式来提高性能?您的还是@ipr101的(https://dev59.com/gWsy5IYBdhLWcg3wxAyO#8333445)? - Eray
1
我的查询和ipr101的查询是相同的。他们只是使用了一个临时表解决方法来避免你报告的错误。顺便说一句,Matthew的解决方案也很好。 ;) - Nicola Cossu

0

您可以先创建子查询的视图,然后通过从该视图中选择进行更新/删除操作。 只需记得在操作完成后删除该视图即可。


0
create table GIVEAWAYS_NEW as(select*from giveaways);

update giveaways set winner=1
where Id=(select max(Id)from GIVEAWAYS_NEW);

3
由于你正在复制整个表格,性能会不会很差? - Pacerier
同时它还存在并发问题:由于您正在使用多个语句,您可能需要将其包装在事务中,否则在“创建表”和“更新”之间,赠品中的最大ID可能会发生变化。 - Doin

0

使用临时表:

如下所示:

UPDATE TABLE_NAME SET TABLE_NAME.IsActive=TRUE
WHERE TABLE_NAME.Id IN (
    SELECT Id
    FROM TEMPDATA
);

CREATE TEMPORARY TABLE TEMPDATA
SELECT MAX(TABLE_NAME.Id) as Id
FROM TABLE_NAME
GROUP BY TABLE_NAME.IncidentId;

SELECT * FROM TEMPDATA;

DROP TABLE TEMPDATA;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接