739

我的数据库中有一个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' 进行更新

我该如何克服呢?

4

16 回答 16

871

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

于 2008-09-05T10:07:48.143 回答
439

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
)

它会做正确的事!

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

于 2012-03-23T17:23:55.737 回答
112

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

UPDATE skills AS s, (SELECT id  FROM skills WHERE type = 'Programming') AS p
SET s.type = 'Development' 
WHERE s.id = p.id;
于 2012-10-04T23:39:44.387 回答
111

您的inner join子查询中的 是不必要的。看起来您想删除不在表story_category中的条目。category_idcategory

做这个:

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);
于 2009-06-03T21:40:18.830 回答
69

如果你做不到

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)

[更新或删除或其他]

于 2017-04-25T11:59:56.287 回答
40
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
)
于 2011-12-23T20:03:08.740 回答
15

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

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

于 2010-09-02T07:31:45.660 回答
14

对于 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;
于 2019-07-10T20:16:10.063 回答
8

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

例子 :

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));
于 2018-01-11T02:14:39.387 回答
5

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

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

于 2008-09-05T10:25:35.383 回答
3

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

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

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

于 2015-01-08T13:18:46.740 回答
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;

它很快。数据越大越好。

于 2009-03-29T02:26:30.380 回答
2

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

于 2016-05-31T11:35:18.000 回答
2

试试这个

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc;
于 2018-01-23T01:14:45.867 回答
1

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

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL
于 2017-05-11T01:55:03.163 回答
1

就问题而言,您要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 版本上运行,也可以在我知道的大多数其他数据库上运行。

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);
| 类别 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)
| 类别 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;
| 类别 ID |
| ----------: |
| 4 |
| 5 |
于 2020-04-10T10:46:12.293 回答