ChatGPT解决这个技术问题 Extra ChatGPT

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

我该如何克服呢?


C
Community

更新:这个答案涵盖了一般错误分类。有关如何最好地处理 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,originally published at Nabble,在这里进行了释义和扩展。


赞成这个答案,因为我必须删除项目并且无法从另一个表中获取信息,必须从同一个表中进行子查询。因为这是在谷歌搜索错误时弹出的内容,所以这对我和很多人在从同一个表进行子查询时尝试更新是最合适的答案。
@Cheekysoft,为什么不将值保存到变量中呢?
@PeterV.Mørch 在 mysqlserverteam.com/derived-tables-in-mysql-5-7 之后,如果执行某些操作,则无法进行合并。例如,为派生的虚拟表提供一个 LIMIT(到 inifity),并且永远不会发生错误。不过,这很骇人听闻,毕竟 MySQL 的未来版本仍然存在支持使用 LIMIT 合并查询的风险。
请您提供有关此解决方法的完整示例吗? UPDATE tbl SET col = ( SELECT ... FROM (SELECT .... FROM) AS x);我仍然收到错误
INNER JOIN 为我工作:)
P
Positive Navid

NexusRex 提供了一个 very good solution,用于从同一个表中通过连接删除。

如果你这样做:

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
)

它会做正确的事!

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


也许是因为我今天陷入困境,但这是最简单的答案,即使它可能不是“最好的”答案。
这很好用,谢谢!那么这里的逻辑是什么?如果它嵌套了一层,那么它将在外部之前执行吗?如果它没有嵌套,那么 mySQL 会在 delete 锁定表后尝试运行它?
这个错误和解决方案没有逻辑意义......但它有效。有时我想知道 MySQL 开发人员正在服用什么药物......
同意@Cerin .. 这完全是荒谬的,但它确实有效。
@ekonoval 谢谢你的解决方案,但这对我来说意义不大,看起来你在愚弄 MySQL,他接受了,哈哈
A
Andy

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

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

这不能写成 UPDATE skills SET type='Development' WHERE type='Programming'; 吗?这似乎没有回答最初的问题。
似乎有点矫枉过正,@lilbyrdie 是正确的 - 它只能是 UPDATE skills SET type='Development' WHERE type='Programming';。我不明白为什么这么多人不考虑他们的工作......
这是最好的答案,恕我直言。它的语法很容易理解,您可以重复使用之前的语句,并且不限于某些超级特定的情况。
即使示例案例有问题,但原则是最好在实际场景中理解和部署。显示的查询有效,因为表列表中的隐式连接为子查询创建了一个临时表,从而提供了稳定的结果并避免了检索和修改同一个表之间的冲突。
不管有人会说这太过分了,它仍然回答了标题方面的问题。在嵌套查询中使用同一个表尝试更新时也会遇到 OP 提到的错误
m
matiaslauriti

您的子查询中的 inner join 是不必要的。您似乎想删除 story_categorycategory_id 不在 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);

做这个:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category);

这应该是最佳答案!也许删除第一个“代替”。
我认为这里不需要 DISTINCT - 为了获得更好的性能;)。
我一定是疯了。答案与原文中所说的相同。
这也是我的答案。不要在 ID 列上where in,因此您不需要子查询主表。
@JeffLowery - 第一个代码块是这里的答案;它是来自问题的第二个代码块。
S
Sequoya

如果你做不到

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)

[更新或删除或其他]


以上所有答案的最容易理解和合乎逻辑的实现。简单明了。
现在这已成为我工作流程的一部分。遇到此错误,请转到此答案并更正查询。谢谢。
现在要对所有事情都使用这个技巧。脑洞大开o_o
很棒的技巧。简单但有用
N
NexusRex
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
)

你能解释一下为什么这样有效吗,为什么再嵌套一层就可以了?这个问题已经作为对@EkoNoval 问题的评论被提出,但没有人回应。也许你可以帮忙。
@AkshayArora,在@Cheekysoft 的回答中浏览标题“也许你可以将表格加入自身”下的部分。他说 UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col - 这将作为此处使用的同一张表的不同别名起作用。同样,在@NexusRex 的回答中,第一个 SELECT 查询充当派生表,其中 story_category 第二次使用。所以OP中提到的错误不应该在这里发生,对吧?
s
sactiw

这就是我为将优先级列值更新 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);

我知道这有点难看,但它确实工作正常。


@anonymous_reviewer:如果对某人的评论给予 [-1] 甚至 [+1],请同时说明你为什么给予它。谢谢!!!
-1,因为这是不正确的。您不能修改在 SELECT 语句中使用的同一个表。
@Chris 我已经在 MySQL 上验证了它,它对我来说很好,所以我会要求你在最后验证它,然后声称它是正确的或不正确的。谢谢!!!
在此页面的底部,它显示“当前,您无法更新表并从子查询中的同一个表中进行选择。” - 我在很多场合都经历过这一点。 dev.mysql.com/doc/refman/5.0/en/update.html
@Chris我知道,但是有一个解决方法,这正是我试图用我的“更新”查询来展示的,相信我它工作得很好。我认为您根本没有真正尝试验证我的查询。
W
Will B.

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

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

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 表中。
Documentation

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

令人惊讶的是,这没有更多的赞成票。还应注意,多表语法也适用于 UPDATE 语句和连接的子查询。允许您执行 LEFT JOIN ( SELECT ... ) 而不是 WHERE IN( SELECT ... ),使实现在许多用例中都很有用。
S
S.Roshanth

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

例子 :

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

Y
YonahW

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

这可能是@Cheekysoft 分两步完成的意思。


b
briankip

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

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

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


l
lipika chakraborty

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


S
Sameer Khanal

尝试这个

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc;

T
Tom Schaefer

如果某些事情不起作用,则通过前门时,请走后门:

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;

它很快。数据越大越好。


而且您刚刚丢失了所有外键,也许您也有一些级联删除。
也许你可以使用这个技巧,但是更新原始表而不是新表,所以你不需要做重命名技巧......
M
Melvin Angelo Jabonillo

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

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 categories.id FROM categories) 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
G
GMB

就问题而言,您希望删除 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 IN 与作为原始表的子查询 JOIN 组合起来似乎不必要地令人费解。这可以用 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 版本上运行,也可以在我知道的大多数其他数据库上运行。

Demo on 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 |