一文詳解小白也能懂的SQL高效去重技巧
生活中的例子
想象你管理一家網(wǎng)店,同一個(gè)訂單(order_number)中的同一商品(product)可能有多次更新記錄(比如庫(kù)存變化、價(jià)格調(diào)整)。你只想查看每個(gè)訂單商品的最新?tīng)顟B(tài),這時(shí)就需要用到"分組取最新記錄"的操作。
原理解析:給數(shù)據(jù)分組并編號(hào)
SELECT *, ROW_NUMBER() OVER ( PARTITION BY order_number, product, craft, trade_name ORDER BY create_time DESC ) AS rn FROM client_product
這個(gè)查詢的核心是ROW_NUMBER()
函數(shù),它像老師給學(xué)生排隊(duì)一樣:
- 分組(PARTITION BY):把相同訂單+產(chǎn)品+工藝+貿(mào)易名稱的記錄分成一組
- 排序(ORDER BY):每組內(nèi)按創(chuàng)建時(shí)間倒序排列(最新時(shí)間排第一)
- 編號(hào)(rn):給每組內(nèi)的記錄標(biāo)記序號(hào)(1,2,3…)
完整查詢解析
SELECT * FROM ( -- 步驟1:給所有記錄標(biāo)記組內(nèi)序號(hào) SELECT *, ROW_NUMBER() OVER ( PARTITION BY order_number, product, craft, trade_name ORDER BY create_time DESC ) AS rn FROM client_product WHERE production_order_number IS NOT NULL -- 排除生產(chǎn)訂單號(hào)為空 AND order_number IS NOT NULL -- 排除訂單號(hào)為空 AND craft != '' -- 排除工藝為空 AND del_flag = '0' -- 只取未刪除記錄 AND deliver_status != '0' -- 排除未交付狀態(tài) ) AS ranked -- 步驟2:只取每組最新記錄 WHERE rn = 1
關(guān)鍵步驟拆解
1.數(shù)據(jù)過(guò)濾(WHERE)
只處理有效數(shù)據(jù):非空訂單號(hào)、有生產(chǎn)訂單號(hào)、工藝不為空、未刪除、已交付
2.分組標(biāo)記(ROW_NUMBER)
訂單號(hào) | 產(chǎn)品 | 創(chuàng)建時(shí)間 | 組內(nèi)序號(hào)(rn) |
---|---|---|---|
A1001 | 手機(jī)殼 | 2023-01-05 | 1(最新) |
A1001 | 手機(jī)殼 | 2023-01-03 | 2 |
B2002 | 數(shù)據(jù)線 | 2023-01-04 | 1(最新) |
3.篩選結(jié)果(WHERE rn=1)
只保留每組中rn=1的記錄,即每個(gè)組合的最新數(shù)據(jù)
實(shí)際應(yīng)用場(chǎng)景
- 訂單管理:獲取每個(gè)訂單的最新?tīng)顟B(tài)
- 設(shè)備監(jiān)控:讀取每個(gè)傳感器的最新讀數(shù)
- 用戶行為:提取每個(gè)用戶最近一次登錄記錄
- 價(jià)格跟蹤:查看每個(gè)商品的最新定價(jià)
性能小貼士
當(dāng)數(shù)據(jù)量很大時(shí):
- 在
order_number, product, craft, trade_name
上創(chuàng)建索引 - 在
create_time
上創(chuàng)建降序索引 - 定期清理歷史數(shù)據(jù)
方法補(bǔ)充
以下是幾種去重的SQL寫法
在 SQL 中,數(shù)據(jù)去重有多種實(shí)現(xiàn)方式,以下是幾種常見(jiàn)寫法及其適用場(chǎng)景:
1. 使用 DISTINCT
關(guān)鍵字
語(yǔ)法:
SELECT DISTINCT column1 [, column2, ...] FROM table_name;
說(shuō)明:直接對(duì)指定字段組合進(jìn)行唯一性篩選,僅保留首次出現(xiàn)的記錄。
示例:
SELECT DISTINCT address FROM student; -- 獲取不重復(fù)的地址
局限性:
- 若對(duì)多字段去重,需所有字段值完全相同才視為重復(fù)。
- 無(wú)法同時(shí)返回非去重字段的原始值,僅能展示去重字段。
2. 使用 GROUP BY
子句
語(yǔ)法:
SELECT column1 [, aggregate_function(column2), ...] FROM table_name GROUP BY column1 [, column2, ...];
說(shuō)明:按指定字段分組,結(jié)合聚合函數(shù)(如 MAX
、MIN
、COUNT
等)獲取其他字段信息。
示例:
SELECT MIN(id), address FROM student GROUP BY address; -- 按地址去重,返回每組最小 id
注意:非聚合字段可能來(lái)自不同記錄,導(dǎo)致數(shù)據(jù)邏輯上不一致(如不同 id
對(duì)應(yīng)同一 address
時(shí),聚合函數(shù)外的字段取值無(wú)明確規(guī)律)。
3. 使用窗口函數(shù)(如 ROW_NUMBER()
)
語(yǔ)法:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY column1 ORDER BY column2) AS rn FROM table_name ) AS t WHERE rn = 1;
說(shuō)明:先按 PARTITION BY
分組,再按 ORDER BY
排序并生成行號(hào),篩選行號(hào)為 1
的記錄。
示例:
SELECT id, name, address FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY address ORDER BY id ASC) AS rn FROM student ) AS a WHERE a.rn = 1; -- 按地址去重,保留每組 id 最小的記錄
優(yōu)勢(shì):可精準(zhǔn)控制保留哪條記錄(如按時(shí)間、ID 排序取最新或最舊),但低版本 MySQL 不支持窗口函數(shù)。
4. 使用 IN
子查詢
語(yǔ)法:
SELECT * FROM table_name WHERE id IN (SELECT MAX(id) FROM table_name GROUP BY column1);
說(shuō)明:通過(guò)子查詢找到每組唯一標(biāo)識(shí)字段(如自增 id
)的最大值,再篩選主表中對(duì)應(yīng)記錄。
示例:
SELECT * FROM student WHERE id IN (SELECT MAX(id) FROM student GROUP BY address); -- 按地址去重,取每組最大 id 的記錄
適用場(chǎng)景:表中存在唯一標(biāo)識(shí)字段(如 id
),且需保留特定條件(如最大 / 最小 id
)的記錄。
5. 使用 NOT EXISTS
語(yǔ)法:
SELECT a.* FROM table_name a WHERE NOT EXISTS ( SELECT 1 FROM table_name b WHERE a.column1 = b.column1 AND a.id < b.id );
示例:
SELECT a.* FROM student a WHERE NOT EXISTS (SELECT 1 FROM student b WHERE a.address = b.address AND a.id < b.id); -- 按地址去重,保留每組 id 最大的記錄
邏輯:對(duì)于每一行 a
,若不存在 b
行(同 column1
且 id
更大),則保留 a
。
6. 使用 UNION
去重
語(yǔ)法:
SELECT column1 [, column2, ...] FROM table_name1 UNION SELECT column1 [, column2, ...] FROM table_name2;
說(shuō)明:合并多個(gè)查詢結(jié)果并自動(dòng)去重(UNION ALL
保留全部記錄,不進(jìn)行去重)。
示例:
SELECT address FROM student UNION SELECT address FROM teacher; -- 合并兩表地址并去重
注意:大數(shù)據(jù)量時(shí)效率較低,建議先用 UNION ALL
再結(jié)合其他方法去重。
7. 使用 INNER JOIN + GROUP BY
語(yǔ)法:
SELECT a.* FROM table_name a INNER JOIN ( SELECT column1, MAX(id) AS max_id FROM table_name GROUP BY column1 ) b ON a.column1 = b.column1 AND a.id = b.max_id;
示例:
SELECT a.* FROM student a INNER JOIN (SELECT address, MAX(id) AS max_id FROM student GROUP BY address) b ON a.address = b.address AND a.id = b.max_id; -- 按地址去重,取每組最大 id 的記錄
邏輯:先通過(guò)子查詢獲取每組最大 id
,再與主表關(guān)聯(lián)篩選。
實(shí)際應(yīng)用中,可根據(jù)數(shù)據(jù)庫(kù)特性(如是否支持窗口函數(shù))、數(shù)據(jù)規(guī)模、業(yè)務(wù)需求(如保留特定記錄)選擇合適的方法。例如,簡(jiǎn)單單字段去重優(yōu)先用 DISTINCT
;需保留其他字段且數(shù)據(jù)一致性要求不高時(shí)用 GROUP BY
;需精準(zhǔn)控制保留記錄時(shí)用窗口函數(shù)或 IN
/NOT EXISTS
等。
總結(jié)
這個(gè)查詢就像給每個(gè)分組內(nèi)的記錄按時(shí)間倒序排隊(duì),然后只取排在第一位的記錄
通過(guò)這個(gè)技巧,你可以輕松地從重復(fù)數(shù)據(jù)中提取最新記錄,讓數(shù)據(jù)清洗和分析變得更高效!下次遇到類似需求時(shí),不妨試試這個(gè)強(qiáng)大的ROW_NUMBER()
函數(shù)吧!
(注:實(shí)際使用時(shí)需根據(jù)業(yè)務(wù)需求調(diào)整分組字段和排序規(guī)則)
到此這篇關(guān)于一文詳解小白也能懂的SQL高效去重技巧的文章就介紹到這了,更多相關(guān)SQL去重內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql行鎖(for update)解決高并發(fā)問(wèn)題
這篇文章主要介紹了mysql行鎖(for update)解決高并發(fā)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08DBeaver連接mysql數(shù)據(jù)庫(kù)圖文教程(超詳細(xì))
本文主要介紹了DBeaver連接mysql數(shù)據(jù)庫(kù)圖文教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07mysql如何根據(jù).frm和.ibd文件恢復(fù)數(shù)據(jù)表
這篇文章主要介紹了mysql根據(jù).frm和.ibd文件恢復(fù)數(shù)據(jù)表的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07mysql 協(xié)議的ping命令包及解析詳解及實(shí)例
這篇文章主要介紹了mysql 協(xié)議的ping命令包及解析詳解及實(shí)例的相關(guān)資料,這里附有簡(jiǎn)單實(shí)例代碼并附下載源碼,需要的朋友可以參考下2017-01-01MySQL中crash safe數(shù)據(jù)完整性機(jī)制面試精講
這篇文章主要為大家介紹了MySQL數(shù)據(jù)完整性crash safe特性面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10