SQL Server 中處理重復(fù)數(shù)據(jù):保留最新記錄的兩種方案
大家在項(xiàng)目開(kāi)發(fā)過(guò)程中,數(shù)據(jù)庫(kù)幾乎是每一個(gè)后端開(kāi)發(fā)者必備的技能,并且經(jīng)常會(huì)遇到對(duì)于數(shù)據(jù)表重復(fù)數(shù)據(jù)的處理,一般需要去除重復(fù)保留最新的記錄。今天這里給大家分享兩種種方案,希望對(duì)大家日常開(kāi)發(fā)能夠提供一些幫助!
首先準(zhǔn)備測(cè)試的數(shù)據(jù)表
創(chuàng)建一個(gè)包含ID, OrderDate, ProductName以及可選的SequenceID的商品購(gòu)買(mǎi)記錄表Sales。
CREATE TABLE Sales
(
ID INT IDENTITY(1,1) PRIMARY KEY,
OrderDate DATE NOT NULL,
ProductName VARCHAR(100) NOT NULL,
SequenceID INT IDENTITY(1,1)
);
-- 訂單日期增加當(dāng)前日期默認(rèn)值約束
ALTER TABLE Sales ADD DEFAULT (GETDATE()) FOR OrderDate;準(zhǔn)備一些測(cè)試數(shù)據(jù)。
INSERT INTO Sales (OrderDate, ProductName)
VALUES
('2023-04-01', '筆記本X1'), -- 示例商品A的最早購(gòu)買(mǎi)日期
('2023-04-07', '智能手機(jī)Y7'),
('2023-04-15', '平板電腦Z3'),
('2023-04-09', '筆記本X1'), -- 商品A的第二次購(gòu)買(mǎi),較早日期
('2023-04-08', '智能手機(jī)Y7'), -- 商品B的第二次購(gòu)買(mǎi),較早日期
('2023-04-20', '平板電腦Z3'), -- 商品C的第二次購(gòu)買(mǎi),較晚日期
('2023-04-18', '筆記本X1'), -- 商品A的第三次購(gòu)買(mǎi),最新日期
('2023-04-22', '智能手機(jī)Y7 Pro'), -- 新產(chǎn)品,不同型號(hào)
('2023-04-25', '平板電腦Z3 Plus'), -- 新產(chǎn)品,不同型號(hào)
('2023-04-24 14:30:00', '筆記本X1'), -- 同日但較早時(shí)間的重復(fù)記錄
('2023-04-24 15:45:00', '筆記本X1'); -- 同日但較晚時(shí)間的記錄,應(yīng)被視為最新查詢(xún)效果如下:

方案一. 使用ROW_NUMBER()函數(shù)刪除重復(fù)項(xiàng)
ROW_NUMBER()函數(shù)是SQL Server中處理重復(fù)數(shù)據(jù)的強(qiáng)大工具之一,可以通過(guò)窗口函數(shù)來(lái)為每一組重復(fù)數(shù)據(jù)分配行號(hào),然后保留每組數(shù)據(jù)中最新的一條記錄。
示例SQL語(yǔ)句:
假設(shè)有一個(gè)表Sales,包含ID, OrderDate, ProductName等字段,其中ID為主鍵,但ProductName和OrderDate上有重復(fù)數(shù)據(jù),我們要保留每個(gè)產(chǎn)品的最新訂單記錄。
-- 查詢(xún)不是最新的重復(fù)記錄直接刪除
WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY ProductName ORDER BY OrderDate DESC) AS RowNum
FROM Sales
)
DELETE FROM CTE
WHERE RowNum > 1;
-- 數(shù)據(jù)庫(kù)不操作直接查詢(xún)每一行不重復(fù)的最新記錄
WITH CTE AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY ProductName ORDER BY OrderDate DESC) AS RowNum
FROM Sales
)
select * FROM CTE
WHERE RowNum = 1;執(zhí)行效果如下:

SQL說(shuō)明:
PARTITION BY ProductName:按照ProductName對(duì)數(shù)據(jù)分組。
ORDER BY OrderDate DESC:在每個(gè)分組內(nèi)按OrderDate降序排序,確保最新記錄排在首位。
ROW_NUMBER():為每組內(nèi)的記錄分配一個(gè)行號(hào),最新的記錄行號(hào)為1。
刪除重復(fù)記錄:在CTE中刪除RowNum大于1的記錄,即除了每個(gè)分組最新的一條記錄外,其余視為重復(fù)并刪除。
直接查詢(xún):針對(duì)CTE篩選RowNum等于1的記錄
方案二. 使用臨時(shí)表的方式
第二種方法是使用臨時(shí)表來(lái)篩選并保留最新記錄。具體步驟如下:
創(chuàng)建臨時(shí)表:首先,創(chuàng)建一個(gè)臨時(shí)表,結(jié)構(gòu)與原表相同,用于存儲(chǔ)去重后的數(shù)據(jù)。
使用MERGE語(yǔ)句:通過(guò)MERGE語(yǔ)句將原表數(shù)據(jù)與臨時(shí)表數(shù)據(jù)進(jìn)行比較,保留每個(gè)唯一標(biāo)識(shí)下的最新記錄。
INSERT INTO #TempSales
SELECT ID, OrderDate, ProductName
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ProductName ORDER BY OrderDate DESC) AS rn
FROM Sales
) t
WHERE t.rn = 1;
select * from #TempSales; -- 直接查詢(xún)就是去重后保留最新記錄的查詢(xún)數(shù)據(jù)
TRUNCATE TABLE Sales; -- 清空原表
-- 重新插入臨時(shí)表的數(shù)據(jù)給Sales。適用數(shù)據(jù)量不是特別大的情況
INSERT INTO Sales
SELECT * FROM #TempSales;
DROP TABLE #TempSales; -- 刪除臨時(shí)表說(shuō)明:
該方案先通過(guò)臨時(shí)表存儲(chǔ)每個(gè)產(chǎn)品的最新記錄,然后清空原表,并將臨時(shí)表中的數(shù)據(jù)重新插入原表,最終達(dá)到保留最新記錄的目的。直接查詢(xún)臨時(shí)表就是所需要的數(shù)據(jù)。




















