You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中基于多条件规则跨type去重的实现方法

跨type多规则去重问题

测试数据建表SQL

with t as (
    select *
    from (
        (
            values ('james', '801xxxxxxx', 'james@gmail.com', 'ca', 'a'),
            ('robert', '714xxxxxxx', '', 'ca', 'a'),
            ('william', '', 'william@gmail.com', '', 'a'),
            ('maria', '1234567890', 'maria@gmail.com', '', 'a'),
            ('richard', '', 'richard@gmail.com', '', 'a'),
            ('', '', 'james@gmail.com', '', 'b'),
            ('maria', '1234567890', '', '', 'b'),
            ('robert', '', '', 'ca', 'b')
        )
    ) t (first_name, phone, email, state, "type")
), a_t as (
    select *
    from t
    where "type" = 'a'
), b_t as (
    select *
    from t
    where "type" = 'b'
)
select *
from t

去重需求

需去除不同type间的重复数据,重复判定满足以下任一规则:

  • 规则1:email字段匹配(空值不视为匹配)
  • 规则2:phone与first_name字段同时匹配(两个字段均非空且对应相等)
  • 规则3:state与first_name字段同时匹配(两个字段均非空且对应相等)

尝试过的无效方案

以下是多种尝试过的去重SQL,但均未得到预期结果:

with t as (
select *
    from (
        (
            values ('james', '801xxxxxxx', 'james@gmail.com', 'ca', 'a'),
            ('robert', '714xxxxxxx', '', 'ca', 'a'),
            ('william', '', 'william@gmail.com', '', 'a'),
            ('maria', '1234567890', 'maria@gmail.com', '', 'a'),
            ('richard', '', 'richard@gmail.com', '', 'a'),
            ('', '', 'james@gmail.com', '', 'b'),
            ('maria', '1234567890', '', '', 'b'),
            ('robert', '', '', 'ca', 'b')
        )
    ) t (first_name, phone, email, state, "type")
),
dedupe_one as (
    select distinct on (email)
        first_name, phone, email, state, "type"
    from t
),
dedupe_two as (
    select distinct on (phone, first_name)
        first_name, phone, email, state, "type"
    from t
),
dedupe_three as (
    select distinct on (state, first_name)
        first_name, phone, email, state, "type"
    from t
),
dedupe_four as (
    select distinct on (email) *
    from t
    union
    select distinct on (phone, first_name) *
    from t
    union
    select distinct on (state, first_name) *
    from t
),
dedupe_five as (
    select distinct on (email) *
    from (
        select distinct on (phone, first_name) *
        from (
            select distinct on (state, first_name) *
            from t
        ) foo2
    ) foo
)
select *
from dedupe_five

正确实现方案

核心思路:先识别跨type的重复记录组,为每组设置优先级(优先保留type='a'的记录),最后筛选出每组内优先级最高的记录。

具体SQL

with t as (
    select *
    from (
        values 
            ('james', '801xxxxxxx', 'james@gmail.com', 'ca', 'a'),
            ('robert', '714xxxxxxx', '', 'ca', 'a'),
            ('william', '', 'william@gmail.com', '', 'a'),
            ('maria', '1234567890', 'maria@gmail.com', '', 'a'),
            ('richard', '', 'richard@gmail.com', '', 'a'),
            ('', '', 'james@gmail.com', '', 'b'),
            ('maria', '1234567890', '', '', 'b'),
            ('robert', '', '', 'ca', 'b')
    ) t (first_name, phone, email, state, "type")
),
-- 给每条记录生成唯一ID,方便关联匹配
record_ids as (
    select *, row_number() over () as rid
    from t
),
-- 找出所有跨type的重复记录对
duplicate_pairs as (
    select distinct
        r1.rid as rid1,
        r2.rid as rid2
    from record_ids r1
    join record_ids r2 on r1."type" != r2."type"
    where 
        -- 规则1:非空email匹配
        (r1.email != '' and r1.email = r2.email)
        -- 规则2:非空phone+first_name同时匹配
        or (r1.phone != '' and r1.first_name != '' and r1.phone = r2.phone and r1.first_name = r2.first_name)
        -- 规则3:非空state+first_name同时匹配
        or (r1.state != '' and r1.first_name != '' and r1.state = r2.state and r1.first_name = r2.first_name)
),
-- 递归找出所有关联的重复记录组
connected_components as (
    select rid as member, rid as group_id from record_ids
    union all
    select cc.member, dp.rid2 as group_id
    from connected_components cc
    join duplicate_pairs dp on cc.group_id = dp.rid1
    where cc.member != dp.rid2
),
-- 为每个记录确定最终的组ID(取组内最小rid作为组标识)
grouped_records as (
    select 
        t.*,
        min(group_id) over (partition by member) as final_group_id
    from record_ids t
    join connected_components cc on t.rid = cc.member
),
-- 为每组记录设置优先级,type='a'优先
ranked_records as (
    select 
        *,
        row_number() over (
            partition by final_group_id 
            order by case when "type" = 'a' then 1 else 2 end
        ) as rn
    from grouped_records
)
-- 筛选每组中优先级最高的记录
select first_name, phone, email, state, "type"
from ranked_records
where rn = 1
order by "type", first_name;

预期结果

执行后将得到以下结果:

first_namephoneemailstatetype
james801xxxxxxxjames@gmail.comcaa
robert714xxxxxxxcaa
williamwilliam@gmail.coma
maria1234567890maria@gmail.coma
richardrichard@gmail.coma

所有type='b'的重复记录均被去除,因为它们与type='a'的记录满足任一去重规则。


内容的提问来源于stack exchange,提问作者Jeong Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 03:25:27