MySQL 查询重复数据只保留最新一条

应用场景

程序实际开发中,总会产生重复的数据,后期优化需要删除重复数据

准备工作

  • 创建测试表
CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `code` varchar(16) NOT NULL,
  `name` varchar(32) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 插入数据
INSERT INTO user (code,name) VALUES('A','张三'),('A','张三'),('A','张三'),('B','李四'),('B','李四'),('B','李四'),('B','李四');

在这里插入图片描述

DDL操作

  • 查询重复数据及重复条数
SELECT id, code, name, count(code) as count
FROM user
GROUP BY code
HAVING count(code) > 1
ORDER BY count DESC;

在这里插入图片描述

  • 查询重复数据,保留最新一条
SELECT *
FROM user
WHERE id NOT IN (
    # 查询重复数据code对应的最大id
    SELECT id
    FROM (
             # 按id倒序排列
             SELECT *
             FROM user
             ORDER BY id DESC
             LIMIT 100000000) r1
    GROUP BY r1.code
    HAVING COUNT(r1.code) > 1)
  and code IN (
    # 查询重复的code
    SELECT code
    FROM user r2
    GROUP BY r2.code
    HAVING COUNT(r2.code) > 1)
ORDER BY id, code;

在这里插入图片描述
上面操作已经拿到重复数据,把最新的数据过滤掉。

<think>首先,用户的问题是关于MySQL查询的:如何在查询数据时只保留最新一条记录。用户说:“我想在MySQL查询数据时,如何只保留最新一条记录”,意思是他们想查询数据,但只返回每个分组的最新记录。 我回顾了提供的引用,这些引用都是关于MySQL查询分组后最新记录的方法。引用[1]、[2]、[3]都提到了类似的技术,比如使用子查询来获取最大时间或使用GROUP BY。引用[4]是关于删除重复记录只保留最新一条,但用户的问题是关于查询,不是删除。引用[5]是关于插入记录的。 关键点: - 用户想查询数据,只保留最新一条记录,通常基于某个分组字段,比如按用户ID分组取最新记录。 - 方法包括使用子查询来获取每个分组的最新时间,然后与主表连接。 根据引用: - 引用[1]:展示了两种方法:一种是先筛选出最大时间和最大ID,然后连表查询;另一种是先排序再分组查询。 - 引用[2]:使用WHERE子句与子查询结合,获取每个分组的最新时间。 - 引用[3]:提到GROUP BY只返回第一条记录,所以需要先排序时间倒序再分组。 我需要基于这些,提供一个清晰、结构化的回答。回答必须使用中文,并遵循系统级指令: - 行内数学表达式用$...$,但这里可能不涉及数学,所以可能不需要。 - 独立公式用$$...$$,但同样,可能不适用。 - LaTeX语法正确。 - 生成相关问题。 - 在引用的段落末尾添加引用标识,比如[^1]。 回答结构: 1. **介绍问题**:解释用户的需求,即在MySQL查询中如何只保留最新一条记录。 2. **提供方法**:详细描述几种常见的方法,包括SQL示例。 3. **解释原理**:简要说明为什么这些方法有效。 4. **注意事项**:提到潜在的问题,如性能或索引优化。 5. **生成相关问题**:在最后添加相关问题部分。 由于用户提到了“保留最新一条记录”,我假设是基于某个分组字段,如按用户ID分组取最新时间戳的记录。 从引用中提取方法: - 方法1:使用子查询获取每个分组的最新时间,然后JOIN。 例如:SELECT * FROM table a JOIN (SELECT group_field, MAX(time_field) AS max_time FROM table GROUP BY group_field) b ON a.group_field = b.group_field AND a.time_field = b.max_time - 方法2:使用子查询在WHERE子句中。 例如:SELECT * FROM table WHERE time_field IN (SELECT MAX(time_field) FROM table GROUP BY group_field) AND group_field IN (SELECT group_field FROM table GROUP BY group_field) - 方法3:使用排序后分组(但MySQL中GROUP BY可能不返回最新记录,除非先排序)。 例如:SELECT * FROM (SELECT * FROM table ORDER BY time_field DESC) AS temp GROUP BY group_field 但方法3在MySQL中可能不可靠,因为GROUP BY的行为取决于模式;在MySQL 8.0之前,如果没有ORDER BY,GROUP BY可能返回任意行。在严格模式下,它可能出错。所以最好使用第一种方法。 在引用[3]中提到:先排序再分组可以取最新一条,但实际上是两次查询。 最佳实践是使用第一种方法。 现在,构建回答: - **开头**:直接回答用户问题。 - **主体**:分步骤解释方法。 - **结尾**:添加相关问题。 确保语言中文,结构清晰。 生成相关问题: - 基于主题,如性能优化、错误处理、其他SQL技巧等。 例如: 1. 如何优化这种查询的性能? 2. 如果表中有重复记录,如何删除只保留最新一条? 3. 在MySQL中,如何查询每组的前N条记录? 引用标识:在描述方法时,如果直接引用站内内容,添加[^1]等。但用户提供的引用是参考,我需要自然地融入。 例如,在描述方法时,可以说:“一种常见的方法是使用子查询来获取每个分组的最新时间戳,然后与主表连接[^1]。” 但引用[1]具体内容是关于t_assistant_article表的查询,所以我需要泛化它。 最后,相关问题部分用§§分隔。</think>### MySQL查询中只保留最新一条记录的方法 在MySQL查询中,只保留每个分组的最新一条记录(例如按用户ID、产品ID等分组),是常见的数据过滤需求。这通常基于时间戳字段(如 `create_time`)或自增ID字段来标识“最新”。以下我将一步步解释可靠的方法,并提供SQL示例。这些方法基于MySQL的GROUP BY、子查询和连接操作,确保查询结果准确且高效。方法的选择取决于表结构和索引优化。 #### 步骤1: 理解核心原理 要只保留最新记录,需要: - 按分组字段(如 `group_field`)分组。 - 在每个分组内,找到最大时间戳或最大ID(标识最新记录)。 - 通过子查询或连接,将主表与这些最大值匹配。 如果表中有索引(如分组字段和时间戳字段),查询性能会显著提升[^1][^2]。 #### 步骤2: 常用方法及SQL示例 以下是三种可靠的方法,每种都适用于不同场景。假设表名为 `your_table`,分组字段为 `group_field`(如用户ID),时间戳字段为 `time_field`(如 `create_time`),主键为 `id`。 **方法1: 使用子查询和JOIN(推荐,高效且标准)** 这种方法先通过子查询获取每个分组的最新时间戳,然后与主表连接。优点是性能好,尤其在大型表中。 ```sql SELECT a.* FROM your_table a JOIN ( SELECT group_field, MAX(time_field) AS max_time FROM your_table GROUP BY group_field ) b ON a.group_field = b.group_field AND a.time_field = b.max_time; ``` - **原理**:子查询先分组并计算每个分组的最大时间戳,然后JOIN确保只返回匹配这些时间戳的记录。 - **优点**:利用索引时效率高,兼容所有MySQL版本[^1][^2]。 - **示例**:假设表 `t_assistant_article`,按 `base_id` 分组取最新 `create_time`: ```sql SELECT a.* FROM t_assistant_article a JOIN ( SELECT base_id, MAX(create_time) AS max_time FROM t_assistant_article GROUP BY base_id ) b ON a.base_id = b.base_id AND a.create_time = b.max_time; ``` **方法2: 使用WHERE子句与子查询(适合简单过滤)** 这种方法在WHERE子句中嵌套子查询,直接过滤出最新记录。语法简单,但可能性能稍差于JOIN。 ```sql SELECT * FROM your_table WHERE time_field IN ( SELECT MAX(time_field) FROM your_table GROUP BY group_field ) AND group_field IN ( SELECT group_field FROM your_table GROUP BY group_field ); ``` - **原理**:第一个子查询获取每个分组的最新时间戳,第二个确保分组字段有效;WHERE子句匹配这些值。 - **优点**:代码直观,易于理解。 - **缺点**:如果表大或索引缺失,可能较慢;需确保子查询不返回NULL[^2][^3]。 - **示例**:按 `name` 分组取最新 `inserttime`: ```sql SELECT * FROM MyClass WHERE inserttime IN ( SELECT MAX(inserttime) FROM MyClass GROUP BY name ) AND name IN ( SELECT name FROM MyClass GROUP BY name ); ``` **方法3: 使用排序后分组(MySQL 8.0+适用,谨慎使用)** 这种方法先对表排序(时间倒序),然后分组。但注意:在MySQL 8.0之前,GROUP BY可能返回任意行,不保证是最新记录;在8.0+中,需配合窗口函数更可靠。 ```sql -- MySQL 8.0以下(不推荐,可能不可靠) SELECT * FROM ( SELECT * FROM your_table ORDER BY time_field DESC ) AS temp GROUP BY group_field; -- MySQL 8.0+(使用ROW_NUMBER()窗口函数,推荐) SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY group_field ORDER BY time_field DESC) AS rn FROM your_table ) AS temp WHERE rn = 1; ``` - **原理**:先按时间倒序排序,分组后取第一条(但旧版MySQL GROUP BY行为不确定);在8.0+中,窗口函数明确指定行号。 - **优点**:窗口函数简洁,适合复杂分组。 - **缺点**:旧版MySQL可能返回错误结果;排序操作可能消耗资源[^3]。 - **示例**:取 `base_id` 分组的最新记录: ```sql -- MySQL 8.0+ 安全方法 SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY base_id ORDER BY create_time DESC) AS rn FROM t_assistant_article ) AS temp WHERE rn = 1; ``` #### 步骤3: 注意事项和优化建议 - **索引优化**:为分组字段(如 `group_field`)和时间字段(如 `time_field`)添加索引,可加速子查询和JOIN操作。例如: ```sql CREATE INDEX idx_group_time ON your_table (group_field, time_field); ``` - **处理重复记录**:如果多个记录有相同时间戳,上述方法可能返回多条。如需唯一记录,可添加主键条件: ```sql SELECT a.* FROM your_table a JOIN ( SELECT group_field, MAX(id) AS max_id -- 使用自增ID确保唯一 FROM your_table GROUP BY group_field ) b ON a.id = b.max_id; ``` - **性能警告**:在大型表中,避免方法3的排序分组;优先使用方法1或窗口函数。测试查询执行计划(EXPLAIN)以优化[^1][^4]。 - **删除重复记录**:如果用户想永久删除旧记录(非查询),可用DELETE语句(参考引用[4]的删除方法)[^4]。 #### 总结 - **推荐方法**:使用方法1(JOIN子查询)或MySQL 8.0+的窗口函数,它们高效且可靠。 - **适用场景**:用户管理、日志分析等需要每组最新数据查询。 - 通过以上步骤,您可轻松实现查询时只保留最新一条记录。如果表结构不同,请提供更多细节以定制解决方案。
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dadeity

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值