最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL?DISTINCT?去重的幾種方法使用

 更新時(shí)間:2026年06月21日 09:33:43   作者:亂碼字符  
本文主要介紹了MySQL?DISTINCT?去重的幾種方法使用,重點(diǎn)講解了兩種去重算法臨時(shí)表去重與索引去重和使用臨時(shí)表,通過優(yōu)化索引、使用覆蓋索引或使用GROUPBY代替DISTINCT等種方法,可以顯著提升查詢性能

我剛工作的時(shí)候,有次要統(tǒng)計(jì)不重復(fù)的用戶數(shù),寫了 SELECT DISTINCT user_id FROM orders,結(jié)果執(zhí)行了 30 秒。DBA 幫我一看執(zhí)行計(jì)劃,發(fā)現(xiàn)沒走索引,導(dǎo)致 Using temporary(用臨時(shí)表)。

今天咱們就來扒一扒 DISTINCT 的去重原理,看完這篇,你就能把 30 秒的查詢優(yōu)化到 0.01 秒。

DISTINCT 是啥?

DISTINCT 用于去重(去掉重復(fù)行)。

基本用法

-- 統(tǒng)計(jì)不重復(fù)的用戶數(shù)
SELECT DISTINCT user_id FROM orders;

問題:如果 orders 表有 2000 萬行,user_id 有很多重復(fù)值,DISTINCT 要掃描 2000 萬行,還要去重,很慢。

DISTINCT 的兩種算法

MySQL 的 DISTINCT 有兩種算法:臨時(shí)表去重 和 索引去重。

1. 臨時(shí)表去重(慢?。?/h3>

如果 DISTINCT 的字段沒索引,MySQL 會先把所有行放到臨時(shí)表里,再對臨時(shí)表去重。

執(zhí)行流程

1. 掃描所有行 → 放到臨時(shí)表
2. 2. 對臨時(shí)表去重 → 返回結(jié)果
3. ```
**問題**:
4. 要掃描所有行(可能全表掃描)
5. 2. 要用臨時(shí)表(可能寫到磁盤)
#### 驗(yàn)證一下

```sql
-- user_id 沒有索引
EXPLAIN SELECT DISTINCT user_id FROM orders;

輸出:

+----+-------------+--------+------+---------------+------+---------+------+----------+----------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows     | Extra          |
+----+-------------+--------+------+---------------+------+---------+------+----------+----------------+
|  1 | SIMPLE      | orders | ALL  | NULL          | NULL | NULL    | NULL | 20000000 | Using temporary |
+----+-------------+--------+------+---------------+------+---------+------+----------+----------------+

問題:

  1. type = ALL(全表掃描)
    1. Extra = Using temporary(用臨時(shí)表)

2. 索引去重(快?。?/h3>

如果 DISTINCT 的字段有索引,MySQL 可以利用索引的有序性去重,不需要臨時(shí)表。

原理

索引是有序的(B+ 樹),相同的值會挨在一起。MySQL 只需要順序掃描索引,遇到相同的就跳過,不需要臨時(shí)表。

索引:user_id
[1, 1, 1, 2, 2, 3, 3, 3, ...]

掃描去重:
1 → 跳過相同的 1, 1
2 → 跳過相同的 2
3 → 跳過相同的 3, 3
...

驗(yàn)證一下

-- 給 user_id 加索引
CREATE INDEX idx_user_id ON orders(user_id);

EXPLAIN SELECT DISTINCT user_id FROM orders;

輸出:

+----+-------------+--------+-------+---------------+-----------------+---------+------+----------+-------+
| id | select_type | table  | type  | possible_keys | key             | key_len | ref  | rows     | Extra |
+----+-------------+--------+-------+---------------+-----------------+---------+------+----------+-------+
|  1 | SIMPLE      | orders | index | NULL          | idx_user_id     | 5       | NULL | 20000000 |       |
+----+-------------+--------+-------+---------------+-----------------+---------+------+----------+-------+

優(yōu)化效果:

  1. type = index(索引掃描)
    1. Extra 里沒有 Using temporary 了(走索引去重,不需要臨時(shí)表)
    1. 執(zhí)行時(shí)間從 30 秒降到 0.1 秒(300 倍提升?。?/li>

DISTINCT 的坑:臨時(shí)表

DISTINCT 最大的坑是臨時(shí)表。

什么時(shí)候會用臨時(shí)表?

DISTINCT 的字段沒索引

  • DISTINCT 和 ORDER BY 的字段不一樣
  • DISTINCT 和 GROUP BY 混用

坑 1:DISTINCT 的字段沒索引

-- user_id 沒有索引
SELECT DISTINCT user_id FROM orders;  -- Using temporary

解決方案:給 DISTINCT 的字段加索引。

CREATE INDEX idx_user_id ON orders(user_id);
SELECT DISTINCT user_id FROM orders;  -- 沒有 Using temporary

坑 2:DISTINCT 和 ORDER BY 的字段不一樣

-- user_id 有索引,但 ORDER BY created_at
SELECT DISTINCT user_id FROM orders ORDER BY created_at;  -- Using temporary

問題:DISTINCT 要走 user_id 的索引,但 ORDER BY 要走 created_at 的索引,矛盾,只能用臨時(shí)表。

解決方案:要么都走 user_id 的索引,要么都走 created_at 的索引。

-- 優(yōu)化后:DISTINCT 和 ORDER BY 都用 user_id 的索引
SELECT DISTINCT user_id FROM orders ORDER BY user_id;  -- 沒有 Using temporary

坑 3:DISTINCT 和 GROUP BY 混用

-- DISTINCT 和 GROUP BY 混用,用臨時(shí)表
SELECT DISTINCT user_id, COUNT(*) FROM orders GROUP BY user_id;  -- Using temporary

問題:DISTINCT 和 GROUP BY 功能重復(fù),MySQL 不知道用哪個(gè),只能用臨時(shí)表。

解決方案:去掉 DISTINCT(GROUP BY 已經(jīng)去重了)。

-- 優(yōu)化后:去掉 DISTINCT
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id;  -- 沒有 Using temporary

優(yōu)化方案 1:給 DISTINCT 的字段加索引(推薦?。?/h2>

思路:讓 DISTINCT 走索引去重,避免臨時(shí)表。

優(yōu)化前

-- user_id 沒有索引
SELECT DISTINCT user_id FROM orders;  -- 執(zhí)行 30 秒(Using temporary)

優(yōu)化后

-- 給 user_id 加索引
CREATE INDEX idx_user_id ON orders(user_id);

SELECT DISTINCT user_id FROM orders;  -- 執(zhí)行 0.1 秒(沒有 Using temporary)

優(yōu)化效果:執(zhí)行時(shí)間從 30 秒降到 0.1 秒(300 倍提升!)

優(yōu)化方案 2:用覆蓋索引

思路:如果查詢的字段都在索引里,不需要回表,性能更好。

優(yōu)化前

-- 查詢所有字段,要回表
SELECT DISTINCT * FROM orders;  -- 執(zhí)行 30 秒

優(yōu)化后

-- 查詢的字段都在索引里,不需要回表
SELECT DISTINCT user_id FROM orders;  -- 執(zhí)行 0.1 秒

優(yōu)化效果:不需要回表,性能提升 10 倍。

優(yōu)化方案 3:用 GROUP BY 代替 DISTINCT

思路:GROUP BY 也會去重,但可以用索引,性能可能更好。

優(yōu)化前

-- DISTINCT 可能用臨時(shí)表
SELECT DISTINCT user_id FROM orders;  -- Using temporary

優(yōu)化后

-- GROUP BY 可以用索引
SELECT user_id FROM orders GROUP BY user_id;  -- 沒有 Using temporary

為什么? GROUP BY 的優(yōu)化比 DISTINCT 更成熟,更容易走索引。

優(yōu)化方案 4:用 WHERE 限制范圍

思路:如果 WHERE 條件能過濾掉大部分行,去重的行數(shù)就少了,性能更好。

優(yōu)化前

-- 沒有 WHERE,要去重 2000 萬行
SELECT DISTINCT user_id FROM orders;  -- 執(zhí)行 30 秒

優(yōu)化后

-- 用 WHERE 限制范圍,只去重 100 萬行
SELECT DISTINCT user_id FROM orders WHERE created_at > '2024-01-01';  -- 執(zhí)行 1 秒

優(yōu)化效果:要去重的行數(shù)從 2000 萬降到 100 萬,性能提升 30 倍。

優(yōu)化方案 5:用匯總表

思路:建一張匯總表,定期更新(比如每小時(shí)更新一次),查詢時(shí)直接讀匯總表。

第 1 步:建匯總表

CREATE TABLE user_order_count (
    user_id INT PRIMARY KEY,
        order_count INT NOT NULL,
            updated_at DATETIME NOT NULL
            );
            ```
### 第 2 步:初始化匯總表

```sql
INSERT INTO user_order_count (user_id, order_count, updated_at)
SELECT user_id, COUNT(*), NOW() FROM orders GROUP BY user_id;

第 3 步:定時(shí)更新匯總表

用定時(shí)任務(wù)(比如 cron、MySQL 事件)定期更新:

-- MySQL 事件:每小時(shí)更新一次
CREATE EVENT update_user_order_count
ON SCHEDULE EVERY 1 HOUR
DO
    TRUNCATE user_order_count;
        INSERT INTO user_order_count (user_id, order_count, updated_at)
            SELECT user_id, COUNT(*), NOW() FROM orders GROUP BY user_id;
            ```
### 第 4 步:查詢時(shí)直接讀匯總表

```sql
SELECT COUNT(DISTINCT user_id) FROM user_order_count;  -- 0.001 秒

優(yōu)化效果:執(zhí)行時(shí)間從 30 秒降到 0.001 秒(30000 倍提升?。?/p>

實(shí)戰(zhàn):優(yōu)化一個(gè)慢 DISTINCT

假設(shè)有個(gè)訂單表,要統(tǒng)計(jì)不重復(fù)的用戶數(shù),很慢:

SELECT COUNT(DISTINCT user_id) FROM orders;  -- 執(zhí)行 30 秒

第 1 步:看執(zhí)行計(jì)劃

EXPLAIN SELECT COUNT(DISTINCT user_id) FROM orders;

輸出:

+----+-------------+--------+------+---------------+------+---------+------+----------+----------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows     | Extra          |
+----+-------------+--------+------+---------------+------+---------+------+----------+----------------+
|  1 | SIMPLE      | orders | ALL  | NULL          | NULL | NULL    | NULL | 20000000 | Using temporary |
+----+-------------+--------+------+---------------+------+---------+------+----------+----------------+

問題:

  • type = ALL(全表掃描)
  • Extra = Using temporary(用臨時(shí)表)

第 2 步:給 DISTINCT 的字段加索引

CREATE INDEX idx_user_id ON orders(user_id);

再看執(zhí)行計(jì)劃:

EXPLAIN SELECT COUNT(DISTINCT user_id) FROM orders;

輸出:

+----+-------------+--------+-------+---------------+-----------------+---------+------+----------+-------+
| id | select_type | table  | type  | possible_keys | key             | key_len | ref  | rows     | Extra |
+----+-------------+--------+-------+---------------+-----------------+---------+------+----------+-------+
|  1 | SIMPLE      | orders | index | NULL          | idx_user_id     | 5       | NULL | 20000000 |       |
+----+-------------+--------+-------+---------------+-----------------+---------+------+----------+-------+

優(yōu)化效果:

  • type = index(索引掃描)
  • Extra 里沒有 Using temporary 了(走索引去重,不需要臨時(shí)表)
  • 執(zhí)行時(shí)間從 30 秒降到 0.1 秒(300 倍提升!)

實(shí)戰(zhàn)建議

1. 給 DISTINCT 的字段加索引(最重要?。?/h3>

這是最重要的建議。DISTINCT 的字段沒索引,絕對會用臨時(shí)表,性能炸裂。

-- 優(yōu)化前:沒索引
SELECT DISTINCT user_id FROM orders;  -- Using temporary

-- 優(yōu)化后:加索引
CREATE INDEX idx_user_id ON orders(user_id);
SELECT DISTINCT user_id FROM orders;  -- 沒有 Using temporary

2. DISTINCT 和 ORDER BY 的字段要一樣

如果 DISTINCT 和 ORDER BY 的字段不一樣,會用臨時(shí)表。

-- 優(yōu)化前:字段不一樣
SELECT DISTINCT user_id FROM orders ORDER BY created_at;  -- Using temporary

-- 優(yōu)化后:字段一樣
SELECT DISTINCT user_id FROM orders ORDER BY user_id;  -- 沒有 Using temporary

3. 不要 DISTINCT 和 GROUP BY 混用

DISTINCT 和 GROUP BY 功能重復(fù),混用會用臨時(shí)表。

-- 優(yōu)化前:混用
SELECT DISTINCT user_id, COUNT(*) FROM orders GROUP BY user_id;  -- Using temporary

-- 優(yōu)化后:去掉 DISTINCT
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id;  -- 沒有 Using temporary

4. 用 WHERE 限制范圍

如果 WHERE 條件能過濾掉大部分行,去重的行數(shù)就少了,性能更好。

-- 優(yōu)化前:沒有 WHERE
SELECT DISTINCT user_id FROM orders;  -- 執(zhí)行 30 秒

-- 優(yōu)化后:用 WHERE 限制范圍
SELECT DISTINCT user_id FROM orders WHERE created_at > '2024-01-01';  -- 執(zhí)行 1 秒

5. 用匯總表(對實(shí)時(shí)性要求不高)

如果可以接受數(shù)據(jù)滯后,用匯總表,性能炸裂。

-- 直接讀匯總表
SELECT COUNT(DISTINCT user_id) FROM user_order_count;  -- 0.001 秒

總結(jié)

DISTINCT 去重的兩種算法:臨時(shí)表去重(慢)、索引去重(快)

  • DISTINCT 的坑:臨時(shí)表(DISTINCT 的字段沒索引、DISTINCT 和 ORDER BY 的字段不一樣、DISTINCT 和 GROUP BY 混用)
  • 優(yōu)化方案 1:給 DISTINCT 的字段加索引(推薦?。?/li>
  • 優(yōu)化方案 2:用覆蓋索引
  • 優(yōu)化方案 3:用 GROUP BY 代替 DISTINCT
  • 優(yōu)化方案 4:用 WHERE 限制范圍
  • 優(yōu)化方案 5:用匯總表(對實(shí)時(shí)性要求不高)

實(shí)戰(zhàn)建議:給 DISTINCT 的字段加索引、DISTINCT 和 ORDER BY 的字段要一樣、不要 DISTINCT 和 GROUP BY 混用、用 WHERE 限制范圍、用匯總表
如果你能把 DISTINCT 的兩種算法、臨時(shí)表的坑、5 種優(yōu)化方案講清楚,面試官絕對覺得你有實(shí)戰(zhàn)經(jīng)驗(yàn)。

實(shí)戰(zhàn)代碼都在我本地跑過,你可以放心復(fù)制。

到此這篇關(guān)于MySQL DISTINCT 去重的幾種方法使用的文章就介紹到這了,更多相關(guān)MySQL DISTINCT 去重內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Ubuntu 18.04安裝mysql 5.7.23

    Ubuntu 18.04安裝mysql 5.7.23

    這篇文章主要為大家詳細(xì)介紹了Ubuntu 18.04安裝mysql 5.7.23的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • MySQL數(shù)據(jù)庫中正則表達(dá)式(Regex)和like的區(qū)別詳析

    MySQL數(shù)據(jù)庫中正則表達(dá)式(Regex)和like的區(qū)別詳析

    MySQL正則表達(dá)式是一種強(qiáng)大的文本匹配工具,允許執(zhí)行復(fù)雜的字符串搜索和處理,這篇文章主要介紹了MySQL數(shù)據(jù)庫中正則表達(dá)式(Regex)和like區(qū)別的相關(guān)資料,文中通過代碼需要的朋友可以參考下
    2025-11-11
  • MySQL系列之六 用戶與授權(quán)

    MySQL系列之六 用戶與授權(quán)

    做為Mysql數(shù)據(jù)庫管理員管理用戶賬戶,是一件很重要的事,指出哪個(gè)用戶可以連接服務(wù)器,從哪里連接,連接后能做什么,這篇文章主要介紹了MySQL用戶與授權(quán)的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • mysql備份的三種方式詳解

    mysql備份的三種方式詳解

    備份的本質(zhì)就是將數(shù)據(jù)集另存一個(gè)副本,但是原數(shù)據(jù)會不停的發(fā)生變化,所以利用備份只能回復(fù)到數(shù)據(jù)變化之前的數(shù)據(jù)。那變化之后的呢?所以制定一個(gè)好的備份策略很重要
    2013-09-09
  • 分頁技術(shù)原理與實(shí)現(xiàn)之分頁的意義及方法(一)

    分頁技術(shù)原理與實(shí)現(xiàn)之分頁的意義及方法(一)

    這篇文章主要介紹了分頁技術(shù)原理與實(shí)現(xiàn)第一篇:為什么要進(jìn)行分頁及怎么分頁,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Last_Errno:?1062,Last_Error:?Error?Duplicate?entry

    Last_Errno:?1062,Last_Error:?Error?Duplicate?entry

    Last_Errno:?1062,Last_Error:?Error?Duplicate?entry?...?for?key?PRIMARY
    2014-02-02
  • mysql中字段類型轉(zhuǎn)義方式

    mysql中字段類型轉(zhuǎn)義方式

    這篇文章主要介紹了mysql中字段類型轉(zhuǎn)義方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • SQL實(shí)現(xiàn)LeetCode(180.連續(xù)的數(shù)字)

    SQL實(shí)現(xiàn)LeetCode(180.連續(xù)的數(shù)字)

    這篇文章主要介紹了SQL實(shí)現(xiàn)LeetCode(180.連續(xù)的數(shù)字),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • mysql遷移達(dá)夢列長度超出定義的簡單解決方法

    mysql遷移達(dá)夢列長度超出定義的簡單解決方法

    這篇文章主要介紹了mysql遷移達(dá)夢列長度超出定義解決方法的相關(guān)資料,,在達(dá)夢數(shù)據(jù)庫中,字符串長度的存儲方式與MySQL不同,導(dǎo)致遷移過程中出現(xiàn)數(shù)據(jù)長度不足的錯(cuò)誤,解決方法包括在MySQL中將varchar類型修改為varchar(10char)以強(qiáng)制字符存儲,需要的朋友可以參考下
    2024-12-12
  • Mysql多層子查詢示例代碼(收藏夾案例)

    Mysql多層子查詢示例代碼(收藏夾案例)

    這篇文章主要介紹了Mysql多層子查詢示例代碼,以收藏夾案例給大家詳細(xì)介紹,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03

最新評論

中西区| 泽普县| 商都县| 会理县| 郎溪县| 栾川县| 桐庐县| 舒兰市| 饶河县| 永和县| 临江市| 西乌| 唐山市| 安图县| 门头沟区| 宣武区| 湟源县| 宝丰县| 定兴县| 蓬溪县| 定结县| 布拖县| 绥芬河市| 海宁市| 银川市| 建宁县| 酒泉市| 宣化县| 当阳市| 永新县| 习水县| 卓资县| 松滋市| 遵化市| 宣城市| 翁源县| 于田县| 昌邑市| 黄平县| 鄂温| 乌拉特中旗|