在 MySQL 中使用 CTE 更新或删除

Posted

技术标签:

【中文标题】在 MySQL 中使用 CTE 更新或删除【英文标题】:Use a CTE to UPDATE or DELETE in MySQL 【发布时间】:2018-11-20 18:55:09 【问题描述】:

新版 mysql 8.0 现已支持Common Table Expressions

根据手册:

允许在 SELECT、UPDATE 和 DELETE 语句的开头使用 WITH 子句:

WITH ... SELECT ...
WITH ... UPDATE ...
WITH ... DELETE ...

所以,我想,给定下表:

ID lastName firstName
----------------------
1  Smith    Pat
2  Smith    Pat
3  Smith    Bob

我可以使用以下查询:

;WITH ToDelete AS 
(
   SELECT ID,
          ROW_NUMBER() OVER (PARTITION BY lastName, firstName ORDER BY ID) AS rn
   FROM mytable
)   
DELETE FROM ToDelete

为了从表中删除重复项,就像我在 SQL Server 中所做的那样。

事实证明我错了。当我尝试从 MySQL Workbench 执行 DELETE 语句时,我收到错误:

错误代码:1146。表 'todelete' 不存在

当我尝试使用 CTE 执行 UPDATE 时,我也会收到一条错误消息。

所以,我的问题是,如何在 MySQL 中的 UPDATEDELETE 语句的上下文中使用 WITH 子句(如 8.0 版手册中所述)?

【问题讨论】:

如果你用SELECT * FROM ToDelete 替换你的删除语句,这是否也会引发错误,或者它是否有效? @Tim 它按预期工作 【参考方案1】:

这似乎是 MySQL 8.x 中已发布的错误。来自这个bug report:

2015版SQL标准中,不能在UPDATE中定义CTE; MySQL 允许它,但将 CTE 设为只读(我们现在正在更新文档以提及这一点)。 这就是说,可以使用视图而不是 CTE;那么视图可能是可更新的,但由于窗口函数的存在,它被具体化为一个临时表(它未合并),因此不可更新(我们也会在文档中提到它)。

以上所有内容也适用于 DELETE。

如果您遵循上述错误链接,您将看到建议使用 CTE 的解决方法,但它涉及以一对一映射将 CTE 连接到原始目标表。根据您的示例,即一揽子删除,尚不清楚您需要什么解决方法来继续使用 CTE 进行删除。

【讨论】:

很好的研究工作! @GiorgosBetsos 顺便说一句,它不是真正的“错误”,而不是我们都知道的 MySQL 中的其他怪癖(例如检查约束,您可以定义,但实际上并没有工作)。这是一个不完整的功能,可能会在以后的版本中完成,我们希望 :-) 我想知道这个“错误”是否也适用于 MariaDB 10.2.1+? @RaymondNijland 好问题,我不知道。如果它是从同一个版本的 MySQL 派生出来的,我会假设是的;在任何情况下,您都可以查看该文档。 @RaymondNijland 我知道你所说的 MariaDB 是什么意思……我记得它比 MySQL 有一些功能。如果它支持基于 SQL Server 样式 CTE 的删除,那就太好了。【参考方案2】:

由于 CTE 不可更新,需要参考原表删除行。我想你正在寻找这样的东西:

WITH ToDelete AS 
(
   SELECT ID,
          ROW_NUMBER() OVER (PARTITION BY lastName, firstName ORDER BY ID) AS rn
   FROM mytable
)   
DELETE FROM mytable USING mytable JOIN ToDelete ON mytable.ID = ToDelete.ID
WHERE ToDelete.rn > 1; 

【讨论】:

这是一个很好的解决方法,但它仍然需要JOIN。我使用CTE(对于这种情况,这是我在SQL Server 中首选的DELETE 方法)的目的是使DELETE 语句尽可能简单。

以上是关于在 MySQL 中使用 CTE 更新或删除的主要内容,如果未能解决你的问题,请参考以下文章

多次创建cte并删除cte [关闭]

用CTE处理表重复数据的更新和删除

在CTE sql的select语句中更新局部变量

mysql数据库外键删除更新规则

如何在 CTE 上的 MySQL 中运行更新查询?

如何限制 MySQL 用户对行执行更新或删除? [复制]