MySQL 刪除重復的行(去重留一)


參見:https://www.mysqltutorial.org/mysql-delete-duplicate-rows/

概括:在這個教程中,你將會學到多種用在 MySQL 中的刪除重復行的方法。

一、准備樣本數據

為了便於演示,我們用下面的腳本創建了表 contacts,並向其中插入了一些樣本數據。

DROP TABLE IF EXISTS contacts;

CREATE TABLE contacts (
    id INT PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL, 
    email VARCHAR(255) NOT NULL
);

INSERT INTO contacts (first_name,last_name,email) 
VALUES ('Carine ','Schmitt','carine.schmitt@verizon.net'),
       ('Jean','King','jean.king@me.com'),
       ('Peter','Ferguson','peter.ferguson@google.com'),
       ('Janine ','Labrune','janine.labrune@aol.com'),
       ('Jonas ','Bergulfsen','jonas.bergulfsen@mac.com'),
       ('Janine ','Labrune','janine.labrune@aol.com'),
       ('Susan','Nelson','susan.nelson@comcast.net'),
       ('Zbyszek ','Piestrzeniewicz','zbyszek.piestrzeniewicz@att.net'),
       ('Roland','Keitel','roland.keitel@yahoo.com'),
       ('Julie','Murphy','julie.murphy@yahoo.com'),
       ('Kwai','Lee','kwai.lee@google.com'),
       ('Jean','King','jean.king@me.com'),
       ('Susan','Nelson','susan.nelson@comcast.net'),
       ('Roland','Keitel','roland.keitel@yahoo.com');

下面這個查詢將返回 contacts 表的數據:

SELECT * FROM contacts
ORDER BY email;

MySQL%20%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E7%9A%84%E8%A1%8C%EF%BC%88%E5%8E%BB%E9%87%8D%E7%95%99%E4%B8%80%EF%BC%89%201d7751cf90204929a639d1126b9328d7/Untitled.png

下面的查詢將返回 contacts 表中重復的 emails:

SELECT 
    email, COUNT(email)
FROM
    contacts
GROUP BY 
    email
HAVING 
    COUNT(email) > 1;

MySQL%20%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E7%9A%84%E8%A1%8C%EF%BC%88%E5%8E%BB%E9%87%8D%E7%95%99%E4%B8%80%EF%BC%89%201d7751cf90204929a639d1126b9328d7/Untitled%201.png

如上圖所示,我們的數據中有 4 行重復的 emails(即有重復的 email 的行)。

二(A)、 使用 DELETE JOIN 語句刪除重復行

MySQL 提供了 DELETE JOIN 語句,這個語句可以使你快速移除重復的行。

下面的語句刪除了重復的行,並且保留了(重復行中)最大的 id。

DELETE t1 FROM contacts t1
INNER JOIN contacts t2
WHERE
    t1.id < t2.id AND
    t1.email = t2.email;

這個查詢引用了兩次 contacts 表,因此,它使用表的別名 t1 和 t2。

上面語句的輸出是:

Query OK, 4 rows affected (0.10 sec)

這表明 4 行數據已經被刪除。你可以執行下面這個查詢再次查找重復的行來驗證刪除的效果:

SELECT 
    email, 
    COUNT(email)
FROM
    contacts
GROUP BY 
    email
HAVING 
    COUNT(email) > 1;

上面這個查詢的返回結果是一個空集,這意味着重復的行已經被刪除了。

讓我們從 contact 表中驗證一下數據:

SELECT * FROM contacts;

MySQL%20%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E7%9A%84%E8%A1%8C%EF%BC%88%E5%8E%BB%E9%87%8D%E7%95%99%E4%B8%80%EF%BC%89%201d7751cf90204929a639d1126b9328d7/Untitled%202.png

可以發現,id 為 2,4,7,9 的行被刪除了。

萬一你想刪除重復的行,同時,你想保留最小的 id,你可以使用下面的語句:

DELETE c1 FROM contacts c1
INNER JOIN contacts c2 
WHERE
    c1.id > c2.id AND 
    c1.email = c2.email;

注意,你可以重新執行腳本來再次建立剛剛的表,然后測試這個查詢。下面的輸出展示了刪除重復的行之后的 contacts 表的數據。

MySQL%20%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E7%9A%84%E8%A1%8C%EF%BC%88%E5%8E%BB%E9%87%8D%E7%95%99%E4%B8%80%EF%BC%89%201d7751cf90204929a639d1126b9328d7/Untitled%203.png

二(B)、使用一個中間表來刪除重復的行

下面展示用一個中間表來刪除重復行的步驟:

  1. 創建一個和你想刪除的重復行的表具有相同結構的新表。
  2. 從原表中向中間表中插入所有不同的(不重復)數據。
  3. 刪除原表,並將中間表重命名為原表的表名。

下面的查詢闡釋了這些步驟:

Step 1. 創建一個和你想要刪除重復行的表具有相同結構的新表:

CREATE TABLE source_copy LIKE source;

Step 2. 從原表中向中間表中插入所有不同的(不重復)數據:

INSERT INTO source_copy
SELECT * FROM source
GROUP BY col; -- 有重復值的列

Step 3. 刪除原表,並將中間表重命名為原表的表名:

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

例如,下面的語句就是從 contacts 表中刪除了具有重復 emails 的行:

-- step 1
CREATE TABLE contacts_temp 
LIKE contacts;

-- step 2
INSERT INTO contacts_temp
SELECT * 
FROM contacts 
GROUP BY email;

-- step 3
DROP TABLE contacts;

ALTER TABLE contacts_temp 
RENAME TO contacts;

二(C)、使用 ROW_NUMBER() 函數刪除重復的行

注意,ROW_NUMBER() 函數是從 MySQL 8.02 版本開始得到支持,所以在你使用這個函數之前,你應該檢查你的 MySQL 版本。

下面的語句使用了 ROW_NUMBER() 函數給每一行分配了一個整數序列值。如果 email 是重復的,那么,行數(即下表中的 row_num)將會比 1 大。

SELECT 
	id, 
    email, 
    ROW_NUMBER() OVER ( 
		PARTITION BY email 
        ORDER BY email
	) AS row_num 
FROM contacts;

下面的語句返回重復行的 id 集合:

SELECT 
	id 
FROM (
	SELECT 
		id,
		ROW_NUMBER() OVER (
			PARTITION BY email
			ORDER BY email) AS row_num
	FROM 
		contacts
) t
WHERE 
	row_num > 1;

MySQL%20%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E7%9A%84%E8%A1%8C%EF%BC%88%E5%8E%BB%E9%87%8D%E7%95%99%E4%B8%80%EF%BC%89%201d7751cf90204929a639d1126b9328d7/Untitled%204.png

然后,你只要使用在 WHERE 分句中帶有一個子查詢的 DELETE 語句從 contacts 表中刪除重復的行即可:

DELETE FROM contacts 
WHERE 
	id IN (
	SELECT 
		id 
	FROM (
		SELECT 
			id,
			ROW_NUMBER() OVER (
				PARTITION BY email
				ORDER BY email) AS row_num
		FROM 
			contacts
		
	) t
    WHERE row_num > 1
);

MySQL 給出了以下的執行后的信息:

4 row(s) affected


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM