如何使用cte删除mysql中的重复数据

Posted

技术标签:

【中文标题】如何使用cte删除mysql中的重复数据【英文标题】:How to delete duplicate data in mysql using cte 【发布时间】:2019-07-25 19:11:19 【问题描述】:

我想删除表中的重复数据。但是我的 sql sript 在查询中返回错误 (1064)。

我在我的 mysql 服务器上尝试了两个单独的 sql 语句,但它一直抛出语法错误。

表格列:

msisdn_lte
batch_id
file_id
date_key
call_timestamp
data_volume
da_value_before_call
da_value_after_call
served_account
source_file_nm
quote
pull_date_time

所有具有完全相同数据的插入 2 次。即每个不同的数据都有两倍

第一句话

DELETE A
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY * ORDER BY call_timestamp) AS rn
FROM broadband_usage_history
) A
WHERE A.rn > 1;

第二句话

WITH usage_cte AS 
(
    SELECT *, ROW_NUMBER() Over(Partition  BY call_timestamp order by call_timestamp) AS row_number
    FROM broadband_usage_history
) 
SELECT * FROM usage_cte ;

我希望获得表中每个数据的一个输入,但我什至无法运行查询。

【问题讨论】:

我什至没有查看您的查询是否有任何语法错误突出,但第一个问题是您使用的是 MySQL 8.0.2 还是更高版本?那是第一次引入像ROW_NUMBER() 这样的窗口函数的时候。 mysqlserverteam.com/mysql-8-0-2-introducing-window-functions 如何定义“重复”?即使在call_timestamp 中,所有列中的数据是否应该相等? @PaulSpiegel 重复,因为我们在所有列中都有相同的数据 【参考方案1】:

如果您可以在所有列中包含具有相同数据的重复行,则无法删除行而不删除其重复项。在这种情况下,CTE 和 ROW_NUMBER() 对您没有帮助,因为您无法从 MySQL 中的 CTE 中删除。带有 CTE 的 JOIN 也无济于事,因为您没有用于 ON 子句的标识列。我看到两种方法可以解决您的问题:

1。创建表副本

创建具有相同架构的新表并仅复制不同的数据。然后删除原始表并重命名新表以替换它:

CREATE TABLE broadband_usage_history_distinct LIKE broadband_usage_history;

INSERT INTO broadband_usage_history_distinct
    SELECT DISTINCT * FROM broadband_usage_history;

DROP TABLE broadband_usage_history;

RENAME TABLE broadband_usage_history_distinct TO broadband_usage_history;

Example on db-fiddle.com

2。创建主键

您的问题是建议始终定义主键的原因之一。好消息 - 创建 AUTO_INCREMENT PRIMARY KEY 永远不会太晚。之后,您可以将其用作 DELETE-JOIN 查询的行标识符:

ALTER TABLE broadband_usage_history
  ADD id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY FIRST;

WITH cte AS (
  SELECT id, ROW_NUMBER() OVER (
    PARTITION BY
      msisdn_lte,
      batch_id,
      file_id,
      date_key,
      call_timestamp,
      data_volume,
      da_value_before_call,
      da_value_after_call,
      served_account,
      source_file_nm,
      quote,
      pull_date_time
    ORDER BY id
  ) AS rn
  FROM broadband_usage_history
)
  DELETE t
  FROM cte
  JOIN broadband_usage_history t USING(id)
  WHERE rn > 1
; 

Example on db-fiddle.com

您还可以使用 GROUP BY 子查询在没有窗口函数的旧版本中删除重复项:

ALTER TABLE broadband_usage_history
  ADD id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY FIRST;

DELETE t
FROM broadband_usage_history t
LEFT JOIN (
  SELECT MIN(id) AS id
  FROM broadband_usage_history
  GROUP BY 
      msisdn_lte,
      batch_id,
      file_id,
      date_key,
      call_timestamp,
      data_volume,
      da_value_before_call,
      da_value_after_call,
      served_account,
      source_file_nm,
      quote,
      pull_date_time
) x USING (id)
WHERE x.id IS NULL;

Example on db-fiddle.com

【讨论】:

【参考方案2】:

您不能按所有列分区,只需将其更改为列应该是唯一的。也不能从子查询中删除。

   DELETE H
    FROM broadband_usage_history H
    INNER JOIN
    (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY Columns_Should_Be_Unique ORDER BY call_timestamp) AS rn
    FROM broadband_usage_history
    ) A
    ON A.Columns_Should_Be_Unique =H.Columns_Should_Be_Unique 
    WHERE A.rn > 1;

【讨论】:

“您不能按所有列进行分区” - 这不准确。如果列出所有列,则可以按所有列进行分区,但不能按* 进行分区。您还可以简化 ON 子句,将其替换为 USING(Columns_Should_Be_Unique) 该语句是在删除重复项的上下文中。如果按所有列分区,您将无法获得重复行的行号。但感谢您的澄清。 我明白“重复”是指所有列中的相同数据。但后来我看到了ORDER BY call_timestamp,这没有任何意义。所以我现在不知道。 @PeterHe 确实没有唯一的列,列中的所有数据都是相同的。这就是为什么我尝试使用调用时间戳但我不断从分区查询中收到语法错误 @Tobbie 我的意思是哪些列应该是唯一的。它们现在是重复的,但是您想删除重复的行,这意味着某些列需要是唯一的,最糟糕的情况是所有列都使记录唯一。

以上是关于如何使用cte删除mysql中的重复数据的主要内容,如果未能解决你的问题,请参考以下文章

mysql怎么去除重复数据

如何使用 group by [重复] 使用 mySQL 删除数据库中的记录

如何获取mysql重复项中的最后一条数据

Mysql数据库中多条重复数据,如何只删除一条?

Oracle中如何删除重复数据

MySQL 处理重复数据:防止表中出现重复数据统计过滤删除重复数据