创建表和测试数据:

-- DROP TABLE IF EXISTS people;

CREATE TABLE people (

id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,

name varchar(50) NOT NULL,

email varchar(100) NOT NULL

);

INSERT INTO people(name, email)

VALUES ('张三', 'zhangsan@test.com'),

('李四', 'lisi@test.com'),

('王五', 'wangwu@test.com'),

('李斯', 'lisi@test.com'),

('王五', 'wangwu@test.com'),

('王五', 'wangwu@test.com');

 其中,2 和 4 的 email 字段存在重复数据;3、5 和 6 的 name 和 email 字段存在重复数据

基于多个字段的重复记录:

如果我们想要找出 name 和 email 两个字段都重复的数据,可以基于这两个字段进行分组统计

SELECT *

FROM people

WHERE (name, email) IN (

SELECT name, email

FROM people

GROUP BY name, email

HAVING count(1) > 1)

ORDER BY email;

 或者:

WITH d AS (

SELECT name, email

FROM people

GROUP BY name, email

HAVING count(*) > 1)

SELECT p.*

FROM people p

JOIN d ON (d.name = p.name AND d.email = p.email)

ORDER BY p.email;

 使用窗口函数查找重复记录:

使用聚合函数查找重复记录需要扫描同一个表两次,如果表中的数据量很大时,可能存在性能问题。为此,我们可以采用另一种方法:窗口函数

首先,我们通过 count() 窗口函数找出每个 email 出现的次数:

SELECT id, name, email, count(*) over (partition by email) cnt

FROM people;

 窗口函数不仅可以找出字段的重复次数,同时还可以保留原表中的数据,避免了二次扫描的操作。接下来我们只需要返回次数大于 1 的记录即可:

WITH d AS (

SELECT id, name, email,

count(*) over (partition by email) cnt

FROM people)

SELECT *

FROM d

WHERE cnt > 1

ORDER BY id;

 窗口函数同样支持基于多个字段的分区操作,以下语句可以用于找出 name 和 email 两个字段都重复的数据:

WITH d AS (

SELECT id, name, email,

count(*) over (partition by name, email) cnt

FROM people)

SELECT *

FROM d

WHERE cnt > 1

ORDER BY id;

 如果需要删除重复记录,请往下看:

使用子查询删除重复记录

假如我们想要删除 email 字段重复的记录,只保留其中 id 最小的一条;可以使用子查询找出需要保留的数据,然后删除其他的数据:

DELETE

FROM

people

WHERE

ID NOT IN ( SELECT MIN ( ID ) FROM people GROUP BY email );

也可以使用跨表删除或者关联子查询删除重复的数据:

DELETE

FROM people p

USING people d

WHERE p.email = d.email AND p.id < d.id;

DELETE

FROM people p

WHERE p.id NOT IN (

SELECT min(id)

FROM people

WHERE email = p.email

);

使用窗口函数删除重复记录

ROW_NUMBER() 窗口函数可以用于将数据进行分组,然后为每一条数据分配一个唯一的数字编号。例如:

SELECT id, name, email,

row_number() over (PARTITION BY email ORDER BY id) AS row_num

FROM people;

 以上语句基于 email 分组(PARTITION BY email),同时按照 id 进行排序(ORDER BY id),然后为每个组内的数据分配一个编号;如果编号大于 1 就意味着存在重复的数据。

我们可以基于该查询结果删除重复的记录:

DELETE

FROM people

WHERE id IN (

SELECT id

FROM (

SELECT id, name, email,

row_number() over (PARTITION BY email ORDER BY id DESC) AS row_num

FROM people) d

WHERE row_num > 1);

参考文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: