искать дубликаты кросс-полей в postgresql и возвращать совпавшие пары - PullRequest
0 голосов
/ 07 августа 2020

У меня есть таблица контактов. Таблица содержит столбец mobile_phone, а также столбец home_phone. Я хотел бы получить все пары повторяющихся контактов, где пара - это два контакта с общим номером телефона.

Обратите внимание: если mobile_phone контакта A совпадает с home_phone контакта B, это также дубликат. Вот пример трех контактов, которые должны совпадать.

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 здесь введите здесь описание ссылки Мне удалось написать запрос, близкий к тому, что я пытаюсь достичь, возвращает список 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 совпадающих контактов в одну строку, как показано выше.

Ответы [ 4 ]

2 голосов
/ 07 августа 2020

Простой запрос будет с оператором перекрытия ARRAY &&:

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 исключает самосоединения и переключение дубликатов.

Но это представление быстро выходит из-под контроля, если многие контакты каким-то образом используют один и тот же номер. *join_collapse_limit объединений. См .:

1 голос
/ 07 августа 2020

Существует упрощенная схема, которая будет короче:

# 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 - пустой. Вы можете отфильтровать результат в соответствии с вашими требованиями c.

Вы также можете использовать array_agg(distinct x), чтобы исключить дубликаты, если таковые имеются.

0 голосов
/ 07 августа 2020

Как насчет этого?

----- 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
0 голосов
/ 07 августа 2020

Одним из простых решений является самосоединение:

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

Это дает вам одну строку на пару «повторяющихся» контактов с контактом, имеющим наименьший идентификатор в первом столбце.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...