【问题标题】:SQL to combine (de-duplicate) Table A based on Table B data?SQL根据表B数据组合(重复)表A?
【发布时间】:2018-10-01 23:04:51
【问题描述】:

如何编写 SQL(SQLite3、postgres 或 MySQL)以 根据表 B 数据组合(去重)表 A?

给定具有电话号码(表格:电话)的人员(表格:联系人), 我想使用电话号码删除重复的联系人。

在这个例子中,公司需要澄清, 真实数据可能没有同一家公司上市,即使 根据电话号码,人是相同的。

注意,在这个例子中,有两个不同的 Betty's,但是 两个查尔斯是同一个人,应该合并。

此外,Ashok 和 Dale 有一个共同的电话号码,因为他们 在同一家公司工作,但不是同一个人。

Table: contacts
===============
id      name      company
1       Ashok     Alpha Co.
2       Betty     Beta Inc.
3       Charles   Cain LLC.          <---|
4       Betty     Delta Corp.            |-- same person
5       Charles   Cain LLC.          <---|
6       Dale      Alpha Co.

Table: phones
============
id      phone_number    contact_id
1       (111) 123-1111  1
2       (111) 123-2222  1
3       (111) 123-3333  1
4       (111) 123-4444  1
5       (222) 456-1111  2
6       (222) 456-2222  2
7       (333) 789-1111  3
8       (333) 789-2222  3
9       (333) 789-3333  3
10      (333) 789-4444  3
11      (444) 456-7777  4
12      (444) 456-8888  4
13      (555) 789-5555  5
14      (333) 789-2222  5
15      (111) 123-3333  6

加入表格给出:

SELECT      c.id, c.name, c.company, p.phone_number, p.contact_id
FROM        contacts as c
INNER JOIN  phones as p
ON          c.id = p.contact_id

c.id    c.name      c.company       p.phone         p.contact_id
1       Ashok       Alpha Co.       (111) 123-1111  1
1       Ashok       Alpha Co.       (111) 123-2222  1
1       Ashok       Alpha Co.       (111) 123-3333  1
1       Ashok       Alpha Co.       (111) 123-4444  1
2       Betty       Beta Inc.       (222) 456-1111  2
2       Betty       Beta Inc.       (222) 456-2222  2
3       Charles     Cain LLC.       (333) 789-1111  3
3       Charles     Cain LLC.       (333) 789-2222  3
3       Charles     Cain LLC.       (333) 789-3333  3
3       Charles     Cain LLC.       (333) 789-4444  3
4       Betty       Delta Corp.     (444) 456-7777  4
4       Betty       Delta Corp.     (444) 456-8888  4
5       Charles     Cain LLC.       (555) 789-5555  5
5       Charles     Cain LLC.       (333) 789-2222  5
6       Dale        Alpha Co.       (111) 123-3333  6

所以我想的是我想遍历所有 的不同电话号码,获取每个联系人的所有联系人 在这些数字中,检查名称是否相同,如果 他们删除了重复的联系人并更改了contact_ids 在电话号码上。

所以结果看起来像:

Table: contacts
===============
id      name      company
1       Ashok     Alpha Co.
2       Betty     Beta Inc.
3       Charles   Cain LLC.
4       Betty     Delta Corp.       <-- Note the duplicate Charles (5) is removed
6       Dale      Alpha Co.

Table: phones
============
id      phone_number    contact_id
1       (111) 123-1111  1
2       (111) 123-2222  1
3       (111) 123-3333  1
4       (111) 123-4444  1
5       (222) 456-1111  2
6       (222) 456-2222  2
7       (333) 789-1111  3
8       (333) 789-2222  3
9       (333) 789-3333  3
10      (333) 789-4444  3
11      (444) 456-7777  4
12      (444) 456-8888  4
13      (555) 789-5555  3         <-- Note the contact_id is updated
15      (111) 123-3333  6         <-- Note the duplicate phone number (14) is removed

c.id    c.name      c.company       p.phone         p.contact_id
1       Ashok       Alpha Co.       (111) 123-1111  1
1       Ashok       Alpha Co.       (111) 123-2222  1
1       Ashok       Alpha Co.       (111) 123-3333  1
1       Ashok       Alpha Co.       (111) 123-4444  1
2       Betty       Beta Inc.       (222) 456-1111  2
2       Betty       Beta Inc.       (222) 456-2222  2
3       Charles     Cain LLC.       (333) 789-1111  3
3       Charles     Cain LLC.       (333) 789-2222  3
3       Charles     Cain LLC.       (333) 789-3333  3
3       Charles     Cain LLC.       (333) 789-4444  3
4       Betty       Delta Corp.     (444) 456-7777  4
4       Betty       Delta Corp.     (444) 456-8888  4
3       Charles     Cain LLC.       (555) 789-5555  3
6       Dale        Alpha Co.       (111) 123-3333  6

【问题讨论】:

    标签: mysql sql postgresql sqlite


    【解决方案1】:

    以下假设您的问题与您所说的一样简单。换句话说,它只是寻找相同的联系人对,而不是遍历可能更复杂的图。

    如果您说任何两个具有相同电话号码(无论他们有多少个)和相同姓名的联系人都是相同的,那么您可以使用以下方法找到他们:

    with cp as (
          select c.*, p.phone_number
          from contacts c join
               phones p
               on c.id = p.contact_id
         )
    select distinct cp.id as id1, cp2.id as id2
    from cp join
         cp cp2
         on cp.phone_number = cp2.phone_number and cp.name = cp2.name and
            cp.id <> cp2.id;
    

    大概,你想保持第一次联系。所以,让我们使用聚合来代替。在这个

    select min(cp.id) as id1, cp2.id as id2
    from cp join
         cp cp2
         on cp.phone_number = cp2.phone_number and cp.name = cp2.name and
             cp.id < cp2.id
    group by cp2.id;
    

    这会产生一对联系人 ID。我们想保留第一个并删除第二个。

    现在,如果我们假设重复只有一层,那么我们可以将其合并到delete

    with cp as (
          select c.*, p.phone_number
          from contacts c join
               phones p
               on c.id = p.contact_id
         )
    delete from contacts
        where id in (select cp2.id
                     from cp join
                          cp cp2
                          on cp.phone_number = cp2.phone_number and cp.name = cp2.name and
                             cp.id < cp2.id
                    );
    

    group by 实际上对于in 来说是不必要的。)。

    注意:这在 MySQL 中不起作用,在 MySQL 中需要使用 JOIN 表示等效逻辑,并且不支持 CTE。

    【讨论】:

    • 如果重复的联系人出现两次以上会有什么不同?
    • @MERM。 . .这取决于它们在电话号码中的表示方式。
    • 同理,我们在原始数据中添加联系人:7 Charles Cain Co. 和电话 16 (333) 789-2222 7
    • 电话号码的重叠:克雷格可能有T1,T2;然后是 T2,T3;然后是 T3、T4。第一个克雷格和最后一个一样吗?这是一个图行走问题。但如果最多有两个重复项,则不会出现。
    • 真实数据中有一个属性表。当为每个属性输入数据时,会添加多个联系人,每个联系人都有一个或多个电话号码。我们想要删除重复项(规范化我们的数据),这样如果任何两个或多个联系人具有相同的姓名和至少一个匹配的电话号码,那么他们被认为是同一个人,我们想要删除除此人的一个实例之外的所有实例,并且将所有唯一的电话号码分配给该人。
    【解决方案2】:

    我认为最简单的方法是分三步:

    1. 执行查询以识别重复记录,只挑选多余的记录并将它们保存到临时表中
    2. 删除此临时表中的联系人
    3. 删除此临时表中的手机

    第一步可以使用这个查询来完成(演示:http://sqlfiddle.com/#!9/a20149/6

    -- CREATE TABLE TEMP_TABLE AS
    SELECT p.id as id_phone,
           p.phone_number,
           p.contact_id,
           c.id as id_contact,
           c.name,
           c.company
    FROM phones p
    JOIN contacts c
    ON p.contact_id = c.id
    WHERE EXISTS (
      SELECT 'Anythng' 
      FROM phones p1
      JOIN contacts c1
      ON p1.contact_id = c1.id
      WHERE p1.phone_number = p.phone_number
        AND c1.name = c.name
        AND c.id < c1.id 
    );
    

    | id_phone |   phone_number | contact_id | id_contact |    name |   company |
    |----------|----------------|------------|------------|---------|-----------|
    |        8 | (333) 789-2222 |          3 |          3 | Charles | Cain LLC. |
    

    然后是步骤 2 和 3:

    DELETE contacts WHERE ID IN (SELECT id_contact FROM temp_table);
    DELETE phones WHERE ID IN (SELECT id_phone FROM temp_table);
    

    【讨论】:

    • 这似乎可行,只是现在所有指向联系人 3 的电话号码都指向一个不存在的联系人。如何将所有这些更改为联系人 5?
    猜你喜欢
    • 2013-11-10
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 2020-07-17
    • 2018-10-01
    • 2021-04-05
    相关资源
    最近更新 更多