干翻全岛蛙蛙 发表于 2024-8-13 05:45:52

MySQL三种去重方式比较

弁言:

在MySQL数据库中,数据去重是一项常见的操作。通过去除重复的数据,可以进步查询效率和数据的整洁度。本文将比较MySQL中常用的三种去重方式,包括使用DISTINCT关键字、使用GROUP BY子句和使用窗口函数ROW_NUMBER(),以资助读者选择最得当自己的去重方法。
三种去重方法介绍:


[*] 使用DISTINCT关键字:
DISTINCT关键字是MySQL提供的一种去重方式。它可以用在SELECT语句中,去除查询结果中的重复记录。使用DISTINCT关键字的语法简朴,只需要在SELECT关键字之后加上DISTINCT即可,这种方式适用于对单个列或多个列举行去重的环境。
SELECT DISTINCT column1, column2 FROM table_name



[*] 优点:
1.语法简单明了,容易理解和使用。
2.可以同时对多个列进行去重,灵活性较好。

[*] 缺点:
1.DISTINCT关键字只能按照select的列进行去重,如果需要查询其他不用去重的列,需要额外处理。


[*]使用GROUP BY子句:
GROUP BY子句是另一种常用的去重方式。它将查询结果按照指定的列举行分组,并对每个分组举行聚合操作。通过使用GROUP BY子句,我们可以将重复的记录分组合并,达到去重的效果。


[*] 优点:
1.可以对多个列进行分组和进行组内聚合操作,提供更灵活的功能。

[*] 缺点:
1.GROUP BY子句的语法比较复杂,学习和理解成本较高。
2.如果需要返回不在group by里面的字段,需要设置sql_model参数。
3.需要注意使用聚合函数处理分组后的数据。


[*] 使用窗口函数ROW_NUMBER():
ROW_NUMBER()函数是SQL中的一个窗口函数,用于为每一行数据分配一个唯一的序列号,当根据需要的字段排序后,取序列号为1的数据,即可完成去重操作。
-- example
SELECT *
        FROM (
           SELECT *, ROW_NUMBER() OVER (PARTITION BY column1, column2, ... ORDER BY column1, column2, ...) AS rn
           FROM table
        ) AS subquery
WHERE rn = 1;




[*] 优点:
1.ROW_NUMBER()函数是SQL标准中提供的窗口函数之一,使用广泛且简单易懂。
2.可以根据需要按照分组后的结果的某列或者某几列再进行组内分组排序,然后通过将ROW_NUMBER()结果为1的行保留下来,可以实现对重复数据的去重操作。
3.通过在ORDER BY子句中指定不同的列和排序顺序,可以灵活地根据具体需求进行去重和排序。

[*] 缺点:
1.只能去重连续行:ROW_NUMBER()函数只能对连续的行(分组后的组内)进行排序和排除,如果要处理非连续(分组后的非组内)的重复行,需要采用其他的方法。
3.语法复杂:语法中涉及到嵌套查询和窗口函数的使用,相对于其他简单的去重方法,可能稍显复杂一些。

效率比较:

首先创建一个测试表,制造一些简朴的测试数据,数据量大概在200w+,如果数据量少很难观察到效果:
-- test.out_order_item definition
CREATE TABLE `out_order_item` (
`id` int NOT NULL AUTO_INCREMENT,
`sale_order_id` int NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
);


[*] 没有索引的环境:
-- group by方式
select
        sale_order_id
from
        test.out_order_item
group by
        sale_order_id
limit 200;
-- 运行10次,2.5s左右
-- limit数量不受影响


-- distinct方式
select
        distinct sale_order_id
from
        test.out_order_item
limit 200;
-- 运行10次,5ms左右
-- limit 20000, 40ms左右
-- limit 2000000, 2.5s左右

-- row_number方式
select
        sale_order_id
from
        (select
                row_number() over(partition by sale_order_id) rn,
                sale_order_id
        from test.out_order_item) a
where
        rn = 1
limit 200;
-- 运行10次,5.5s左右
-- limit数量不受影响

[*] 有索引的环境:
先给表字段sale_order_id添加索引
CREATE INDEX sale_order_id_IDX USING BTREE ON test.out_order_item (sale_order_id);
-- group by方式
select
        sale_order_id
from
        test.out_order_item
group by
        sale_order_id
limit 200;
-- 运行10次,1ms左右
-- limit 20000,40ms左右
-- limit 2000000,1.2s左右


-- distinct方式
select
        distinct sale_order_id
from
        test.out_order_item
limit 200;
-- 运行10次,1ms左右
-- limit 20000, 40ms左右
-- limit 2000000, 1.5s左右

-- row_number方式
select
        sale_order_id
from
        (select
                row_number() over(partition by sale_order_id) rn,
                sale_order_id
        from test.out_order_item) a
where
        rn = 1
limit 200;
-- 运行10次,5.5s左右
-- limit数量不受影响

结论:

从上面的结果的执行时间可以看出,三种去重方法可以适用于不同的场景,而且其效率也不相同。


[*] 适用场景:

[*]distinct:简朴的字段去重,不需要其他操作;
[*]group by:需要举行额外的聚合处置惩罚;
[*]row_number():想要返回非去重的其他列;

[*] 效率影响:

[*]无索引环境
[*]distinct:取决于返回结果集的巨细,数目远小于表数目时性能最好;
[*]group by:不受结果集影响,性能中等;
[*]row_number():不受结果集影响,性能最差;

[*]有索引环境
[*]distinct:取决于返回结果集的巨细,结果集少时与group by相差不大;
[*]group by:取决于返回结果集的巨细,结果集大时,比distinct性能好;
[*]row_number():不受结果集影响,性能最差;


以上是简朴的从现象得出的结论,有爱好研究其缘故起因的小伙伴可以通过explain关键字看看执行计划,分析为何出现这种环境,这里就先不睁开具体分析。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: MySQL三种去重方式比较