mysql 一个较特殊的问题:You can't specify target table '表名' for update in FROM clause

今天在写 mysql 遇到一个比较特殊的问题。

mysql 语句如下:

update 表名 set cabf_enabled=0

where cabf_id in (

SELECT 表名.cabf_id FROM 表名

Inner Join wms_cabinet ON 表名.cabf_cab_id = wms_cabinet.cab_id

Inner Join wms_cabinet_row ON wms_cabinet.cab_row_id =wms_cabinet_row.row_id

where wms_cabinet_row.row_site_id=27 and 表名.cabf_enabled=1)

运行时提出如下提示: You can't specify target table '表名' for update in FROM clause

运行 in 里面的 select 字句:

 

SELECT 表名.cabf_id FROM 表名

Inner Join wms_cabinet ON 表名.cabf_cab_id = wms_cabinet.cab_id

Inner Join wms_cabinet_row ON wms_cabinet.cab_row_id =wms_cabinet_row.row_id

where wms_cabinet_row.row_site_id=27 and 表名.cabf_enabled=1

 

可以正确 select 正确结果。再把结果直接写到 in 里面,改后语句如下:
update 表名 set cabf_enabled=0 where cabf_id in ('113','114','115'),再运行可以正确执行更新。

到这一步开始想不明白,为什么用 select 子句运行会出错呢?以前在 mssql 这种写法是很常见的。
没办法了,唯有动用 baidu。找到两条记录。

原来原因是:mysql中不能这么用。 (等待mysql升级吧)。那串英文错误提示就是说,不能先select出同一表中的某些值,

再update这个表(在同一语句中)。 也找到替代方案,重写改写了 sql 。

 

改写后的 sql 如下所示,大家仔细区别一下。

 

update 表名 set cabf_enabled=0 where cabf_id in (

SELECT a.cabf_id FROM (select tmp.* from 表名 tmp) a

Inner Join wms_cabinet b ON a.cabf_cab_id = b.cab_id

Inner Join wms_cabinet_row c ON b.cab_row_id = c.row_id

where c.row_site_id=29 and a.cabf_enabled=1)

重点在 SELECT a.cabf_id FROM (select tmp.* from 表名 tmp) a ,我 select tmp.* from 表名 tmp 作为子集,

然后再 select a.cabf_id FROM 子集,这样就不会 select 和 update 都是同一个表。致此问题得到完美解决。

<think>嗯,我现在遇到了一个SQL错误:“You can't specify target table for update in FROM clause”。这到底是什么意思呢?好像是说在更新语句中不能直接使用目标作为子查询。我记得之前遇到过类似的问题,可能和MySQL的限制有关。让我仔细想想。 根据用户提供的引用,比如引用[1]和[3],他们提到在DELETE或UPDATE语句中,如果子查询直接引用了要操作的MySQL会报这个错误。解决方法好像是将子查询再嵌套一层,变成一个中间。比如在引用[1]中,他们先用子查询选出min(id),然后再用这个结果作为中间,外面再包一层SELECT,这样就能避免错误了。 那具体应该怎么做呢?比如用户现在想更新message的content字段,条件是id在某个子查询的结果里。如果直接写成UPDATE message SET ... WHERE id IN (SELECT min(id) FROM message GROUP BY uid),这就会报错,因为子查询直接引用了要更新的message。这时候需要把子查询的结果作为中间,比如写成SELECT min_id FROM (SELECT min(id) AS min_id FROM message GROUP BY uid) AS a。这样,外层的UPDATE语句就不再直接引用原,而是引用一个派生a,从而绕过限制。 那这个解决方法的原理是什么呢?可能是因为MySQL不允许在UPDATE或DELETE的子查询中直接引用正在被修改的,以防止数据不一致。通过嵌套子查询,相当于创建了一个临时,这样MySQL就不会认为子查询直接依赖原,从而允许执行。 另外,引用[3]和[4]中的例子也是类似的。在删除重复数据时,子查询需要嵌套一层,比如DELETE FROM Person WHERE id NOT IN (SELECT id FROM (SELECT min(id) FROM Person GROUP BY email) AS m)。这里内部子查询的结果被当作临时m,外层再从中选择id,这样就能避免错误。 总结一下,解决步骤应该是:首先,在子查询中将原的结果包装成一个派生,然后再在外部查询中使用这个派生。这样MySQL就不会报错了。不过需要注意,不同的数据库可能有不同的处理方式,比如在SQL Server中可能需要不同的方法,但用户的问题集中在MySQL上,所以应该按照MySQL的解决方法来做。 现在,用户需要解决的是UPDATE语句中的这个问题,所以正确的做法应该是将子查询部分用额外的SELECT包裹,形成中间。例如: UPDATE message SET content = 'Hello World' WHERE id IN ( SELECT min_id FROM ( SELECT min(id) AS min_id FROM message GROUP BY uid ) AS a ); 这样就能正确执行而不报错了。同时,用户可能需要确保在子查询中正确使用别和列,避免语法错误。另外,还要注意性能问题,因为嵌套子查询可能会影响执行效率,尤其是在处理大数据量时,可能需要进一步优化。 可能还需要提醒用户,不同版本的MySQL可能有不同的行为,但大多数情况下,这种解决方法都是有效的。此外,如果用户遇到其他类似错误,比如在DELETE语句中,同样的方法也适用,只需要将子查询嵌套一层即可。</think>### 解决SQL错误"You can't specify target table for update in FROM clause"的方法 #### 问题原因 MySQL不允许在UPDATE或DELETE语句的子查询中直接引用**正在被操作的**。这种限制是为了避免数据更新过程中的逻辑冲突和不确定性[^1][^3]。 #### 解决方案 通过嵌套子查询创建中间,解除对原的直接引用。以下是具体实现步骤: 1. **原始错误示例** ```sql UPDATE message SET content = 'Hello World' WHERE id IN (SELECT MIN(id) FROM message GROUP BY uid); ``` 2. **正确修改方法** ```sql UPDATE message SET content = 'Hello World' WHERE id IN ( SELECT min_id FROM ( SELECT MIN(id) AS min_id -- 创建中间 FROM message GROUP BY uid ) AS a -- 必须使用别 ); ``` #### 实现原理 通过两层嵌套子查询: 1. 内层`SELECT`生成临时结果集 2. 外层`SELECT`将其转换为中间 3. 外层`UPDATE`通过中间间接访问数据 #### 其他应用场景 此方法同样适用于DELETE操作: ```sql DELETE FROM Person WHERE id NOT IN ( SELECT id FROM ( SELECT MIN(id) AS id -- 中间 FROM Person GROUP BY email ) AS m ); ``` #### 注意事项 - 必须为嵌套子查询指定别(如`AS a`) - 确保子查询字段称与外部查询匹配 - 大数据量时注意性能优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值