我的数据库中有一个表 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' 进行更新
我该如何克服呢?
更新:这个答案涵盖了一般错误分类。有关如何最好地处理 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,在这里进行了释义和扩展。
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(这会导致它因错误而失败),但第二个查询不符合派生合并优化的条件时间>。因此,优化器被迫首先执行子查询。
最近我不得不更新同一张表中的记录,如下所示:
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';
吗?这似乎没有回答最初的问题。
UPDATE skills SET type='Development' WHERE type='Programming';
。我不明白为什么这么多人不考虑他们的工作......
您的子查询中的 inner join
是不必要的。您似乎想删除 story_category
中 category_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
- 为了获得更好的性能;)。
where in
,因此您不需要子查询主表。
如果你做不到
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)
[更新或删除或其他]
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
)
UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col
- 这将作为此处使用的同一张表的不同别名起作用。同样,在@NexusRex 的回答中,第一个 SELECT
查询充当派生表,其中 story_category
第二次使用。所以OP中提到的错误不应该在这里发生,对吧?
这就是我为将优先级列值更新 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);
我知道这有点难看,但它确实工作正常。
对于 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 ... )
,使实现在许多用例中都很有用。
最简单的方法是在子查询中引用父查询表时使用表别名。
例子 :
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));
您可以将所需行的 ID 插入到临时表中,然后删除在该表中找到的所有行。
这可能是@Cheekysoft 分两步完成的意思。
根据@CheekySoft 链接的 the Mysql UPDATE Syntax,它在底部显示。
目前,您无法在子查询中更新表并从同一个表中进行选择。
我猜您正在从 store_category 中删除,同时仍在联合中从中进行选择。
尝试将 Select 语句的结果保存在单独的变量中,然后将其用于删除查询。
尝试这个
DELETE FROM story_category
WHERE category_id NOT IN (
SELECT DISTINCT category.id
FROM (SELECT * FROM STORY_CATEGORY) sc;
如果某些事情不起作用,则通过前门时,请走后门:
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;
它很快。数据越大越好。
这个查询怎么样希望它有帮助
DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL
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
就问题而言,您希望删除 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 版本上运行,也可以在我知道的大多数其他数据库上运行。
-- 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 |