參見: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;
下面的查詢將返回 contacts 表中重復的 emails:
SELECT
email, COUNT(email)
FROM
contacts
GROUP BY
email
HAVING
COUNT(email) > 1;
如上圖所示,我們的數據中有 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;
可以發現,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 表的數據。
二(B)、使用一個中間表來刪除重復的行
下面展示用一個中間表來刪除重復行的步驟:
- 創建一個和你想刪除的重復行的表具有相同結構的新表。
- 從原表中向中間表中插入所有不同的(不重復)數據。
- 刪除原表,並將中間表重命名為原表的表名。
下面的查詢闡釋了這些步驟:
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;
然后,你只要使用在 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