我的数据库中有一个表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”

我如何克服这一问题?


当前回答

如果Priority列值在表中大于等于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);

我知道它有点难看,但它确实很好。

其他回答

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

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

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

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

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

就问题而言,您希望删除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
);

将NOT IN与JOIN原始表的子查询组合在一起似乎不太复杂。这可以用不存在和相关子查询以更直接的方式表示:

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

这个quer可以在任何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 |

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

例子:

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

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

示例技术来自于最初发表于Nabble的巴伦·施瓦茨(Baron Schwartz),此处对其进行了解释和扩展。