PostgreSQL 查找重复数据(二)

创建表和测试数据:


-- 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);

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值