在postgresql中搜索跨字段重复项并带回匹配对

p1iqtdky  于 2021-07-24  发布在  Java
关注(0)|答案(4)|浏览(234)

我有一张联系人表。该表包含一个mobile\u phone列和一个home\u phone列。我想获取所有成对的重复联系人,其中一对是两个联系人共享一个电话号码。
请注意,如果联系人a的移动电话与联系人b的家庭电话匹配,则这也是一个副本。下面是三个应该匹配的联系人的示例。

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

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

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

借助@erwin here enter link description here,我可以编写一个接近我要实现的目标的查询,返回列表中所有联系人的联系人ID列表,这些联系人与列表中的其他联系人共享一个电话号码。

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

输出只包含如下联系人ID。。。

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

我想将匹配联系人的联系人ID放在一行中,如上所示。

vltsax25

vltsax251#

一个简单的查询是使用array overlap操作符 && :

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 < c2.contact_id 排除自联接和切换的重复项。
但是,如果许多联系人以某种方式共享相同的号码,这种表示很快就会失控。
旁白:条件 [INNER] JOIN 以及 WHERE 条件完全相同,但不超过 join_collapse_limit 涉及连接。请参见:
有条件的大表联接的计数很慢

n3schb8v

n3schb8v2#

简化的模式更短:


# 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) 排除重复项(如有)。

epfja78i

epfja78i3#

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

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最小。

5jdjgkvh

5jdjgkvh4#

这个怎么样?

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

相关问题