【问题标题】:search for cross-field duplicates in postgresql and bring back matched pairs在 postgresql 中搜索跨字段重复项并带回匹配的对
【发布时间】:2020-11-27 06:48:02
【问题描述】:

我有一张联系人表。该表包含一个 mobile_phone 列以及一个 home_phone 列。我想获取所有重复的联系人对,其中一对是共享一个电话号码的两个联系人。

请注意,如果联系人 A 的 mobile_phone 与联系人 B 的 home_phone 匹配,则这也是重复的。 这是应该匹配的三个联系人的示例。

contact_id|mobile_phone|home_phone|other columns such as email.......|...
-------------------------------------------------------------------------
111       |9748777777  |1112312312|..................................|...
112       |1112312312  |null      |..................................|...
113       |9748777777  |0001112222|..................................|...

具体来说,我想带回一个表格,其中每一行都包含两个匹配联系人的contact_ids。例如,

||contact_id_a|contact_id_b||
||-------------------------||
||   145155   |   145999   ||
||   145158   |   145141   ||

在@Erwin 这里enter link description here 的帮助下,我能够编写一个接近我想要实现的查询,带回列表中与其他联系人共享电话号码的所有联系人的contact_ids 列表列表。

SELECT c.contact_id
FROM   contacts c
WHERE  EXISTS (
   SELECT FROM contacts x
   WHERE (x.data->>'mobile_phone' is not null and x.data->>'mobile_phone' IN (c.data->>'mobile_phone', c.data->>'home_phone'))
       OR (x.data->>'home_phone' is not null and x.data->>'home_phone'   IN (c.data->>'mobile_phone', c.data->>'home_phone'))
   AND x.contact_id <> c.contact_id  -- except self
   );

输出只包含这样的contact_ids...

||contact_id||
--------------
||  2341514 ||
||  345141  ||

我想将匹配联系人的contact_ids 带回一行中,如上所示。

【问题讨论】:

  • 如果 10 个联系人以某种方式共享同一个电话号码,如果我们不包括交换的重复项,那么这将是 45 (10*9/2) 行成对。这就是你想要的吗?

标签: sql postgresql duplicates


【解决方案1】:

一个简单的查询是ARRAY overlap operator &amp;&amp;:

SELECT c1.contact_id AS a, c2.contact_id AS b
FROM   contacts c1
JOIN   contacts c2 ON c1.contact_id < c2.contact_id
WHERE  ARRAY [c1.mobile_phone, c1.home_phone] && ARRAY[c2.mobile_phone, c2.home_phone];

条件c1.contact_id &lt; c2.contact_id 不包括自加入和切换重复。

但如果许多联系人以某种方式共享同一个号码,这种表示很快就会失控。

除此之外:[INNER] JOINWHERE 条件的条件完全一样,但只涉及 join_collapse_limit 连接。见:

【讨论】:

    【解决方案2】:

    有更短的简化架构:

    # with t(x,p1,p2) as (values(1,1,2),(2,2,null),(3,1,3),(4,2,5))
    select array_agg(x), p
    from t cross join lateral (values(t.p1),(t.p2)) as pp(p)
    group by p;
    ┌───────────┬──────┐
    │ array_agg │  p   │
    ├───────────┼──────┤
    │ {2}       │ ░░░░ │
    │ {1,3}     │    1 │
    │ {3}       │    3 │
    │ {4}       │    5 │
    │ {1,2,4}   │    2 │
    └───────────┴──────┘
    

    这意味着:联系人1和3共享手机1,联系人1,2和4共享手机2,手机3只与联系人3相关,联系人4只有一个拥有手机5的人,而联系人2的手机是空的。您可以根据您的特定要求过滤结果。

    您也可以使用array_agg(distinct x) 排除重复项(如果有)。

    【讨论】:

      【解决方案3】:

      一个简单的解决方案是自联接:

      select c1.contact_id contact1, c2.contact_id contact2
      from conctacts c1
      inner join contacts c2
          on c1.contact_id < c2.contact_id
          and (
              least(c1.data->>'mobile_phone', c1.data->>'home_phone') = least(c2.data->>'mobile_phone', c2.data->>'home_phone')
              or greatest(c1.data->>'mobile_phone', c1.data->>'home_phone') = greatest(c2.data->>'mobile_phone', c2.data->>'home_phone')
          )
      
      

      这为每对“重复”联系人提供一行,第一列中 id 最小的联系人。

      【讨论】:

      • 我修正了一些拼写错误并稍微改变了逻辑。这行得通... 从联系人 c1 中选择 c1.contact_id contact1, c2.contact_id contact2 在 c1.contact_id >'mobile_phone' in (c2.data->> 'mobile_phone', c2.data->>'home_phone')) 或 (c1.data->>'home_phone' in (c2.data->>'mobile_phone', c2.data->>'home_phone')))
      【解决方案4】:

      这个怎么样?

      ----- setup sample data
      CREATE TABLE CUSTOMER (
         ID       INT PRIMARY KEY  NOT NULL,
         HOME     TEXT,
         MOBILE   TEXT    
      );
      
      INSERT INTO CUSTOMER (ID, HOME, MOBILE) VALUES (1, '123', NULL);
      INSERT INTO CUSTOMER (ID, HOME, MOBILE) VALUES (2, '123', '123');
      INSERT INTO CUSTOMER (ID, HOME, MOBILE) VALUES (3, '124', '123');
      INSERT INTO CUSTOMER (ID, HOME, MOBILE) VALUES (4, NULL, '222');
      
      ----- find matches
      WITH cte (ID, PHONE) AS (
       SELECT ID, HOME   FROM CUSTOMER WHERE HOME   <> '' 
       UNION
       SELECT ID, MOBILE FROM CUSTOMER WHERE MOBILE <> ''
      )
      SELECT DISTINCT c1.id, c2.id 
      FROM 
          cte c1
          INNER JOIN cte c2   ON  c1.id < c2.id  AND  c1.PHONE = c2.PHONE
      

      【讨论】:

        猜你喜欢
        • 2023-03-03
        • 2019-09-04
        • 2013-07-29
        • 1970-01-01
        • 1970-01-01
        • 2021-07-11
        • 2014-07-10
        • 1970-01-01
        • 2015-12-23
        相关资源
        最近更新 更多