Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何使用子表记录检测重复记录_Sql - Fatal编程技术网

Sql 如何使用子表记录检测重复记录

Sql 如何使用子表记录检测重复记录,sql,Sql,假设我正在创建一个地址簿,其中主表包含基本联系信息和电话号码子表- Contact =============== Id [PK] Name PhoneNumber =============== Id [PK] Contact_Id [FK] Number 因此,联系人记录在PhoneNumber表中可能有零个或多个相关记录。除了主键之外,对任何列的唯一性都没有约束。事实上,这一定是真的,因为: 两个姓名不同的联系人可能共享一个电话号码,并且 两个联系人的姓

假设我正在创建一个地址簿,其中主表包含基本联系信息和电话号码子表-

Contact
===============
Id         [PK]
Name

PhoneNumber
===============
Id         [PK]
Contact_Id [FK]
Number
因此,联系人记录在PhoneNumber表中可能有零个或多个相关记录。除了主键之外,对任何列的唯一性都没有约束。事实上,这一定是真的,因为:

  • 两个姓名不同的联系人可能共享一个电话号码,并且
  • 两个联系人的姓名可能相同,但电话号码可能不同
  • 我想将一个可能包含重复记录的大型数据集导入我的数据库,然后使用SQL过滤掉重复记录。识别重复记录的规则很简单。。。他们必须共享相同的名称和相同数量的具有相同内容的电话记录

    当然,这对于从联系人表中选择重复项非常有效,但根据我的规则,这无助于检测实际的重复项:

    SELECT * FROM Contact
    WHERE EXISTS
        (SELECT 'x' FROM Contact t2 
         WHERE t2.Name = Contact.Name AND
               t2.Id > Contact.Id);
    
    似乎我想要的是我已经拥有的东西的逻辑延伸,但我必须忽略它。有什么帮助吗

    谢谢

    关键词“拥有”是你的朋友。一般用途是:

    select field1, field2, count(*) records
    from whereever
    where whatever
    group by field1, field2
    having records > 1
    
    是否可以在having子句中使用别名取决于数据库引擎。你应该能够将这一基本原则应用于你的情况。

    关键词“拥有”是你的朋友。一般用途是:

    select field1, field2, count(*) records
    from whereever
    where whatever
    group by field1, field2
    having records > 1
    

    是否可以在having子句中使用别名取决于数据库引擎。你应该能够将这一基本原则应用于你的情况。

    作者将“两个人是同一个人”的要求表述为:

  • 同名
  • 拥有相同数量的电话号码所有电话号码都相同
  • 所以这个问题比看起来要复杂一点(或者我只是想得太多了)

    示例数据和(我知道这是一个丑陋的查询,但总体思路是存在的)一个示例查询,我在下面的测试数据上进行了测试,该数据似乎工作正常(我使用的是Oracle 11g R2):

    在SQL FIDLE上检查:

    已编辑

    对作者评论的回答:当然我没有考虑到这一点。。。下面是一个经过修改的解决方案:

    -- new test data
    INSERT INTO contact (id, name) VALUES (8, 'Jane');
    INSERT INTO contact (id, name) VALUES (9, 'Jane');
    
    SELECT c1_id, name
      FROM (
        SELECT c1.id AS c1_id, c1.name, c2.id AS c2_id, COUNT(1) AS cnt
          FROM contact c1
            JOIN contact c2 ON (c2.id != c1.id AND c2.name = c1.name)
            LEFT JOIN phone_number pn ON (pn.contact_id = c1.id)
        WHERE pn.contact_id IS NULL
          OR EXISTS (SELECT 1
                    FROM phone_number
                  WHERE contact_id = c2.id
                    AND phone = pn.phone)
        GROUP BY c1.id, c1.name, c2.id
      )
    WHERE (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id) IN (0, cnt)
      AND (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id) = (SELECT COUNT(1) FROM phone_number WHERE contact_id = c2_id)
    ;
    

    我们允许没有电话号码(左连接)的情况,在外部查询中,我们现在比较用户的电话号码-它必须等于0,或者是从内部查询返回的号码。

    作者将“两个人是同一个人”的要求表述为:

  • 同名
  • 拥有相同数量的电话号码所有电话号码都相同
  • 所以这个问题比看起来要复杂一点(或者我只是想得太多了)

    示例数据和(我知道这是一个丑陋的查询,但总体思路是存在的)一个示例查询,我在下面的测试数据上进行了测试,该数据似乎工作正常(我使用的是Oracle 11g R2):

    在SQL FIDLE上检查:

    已编辑

    对作者评论的回答:当然我没有考虑到这一点。。。下面是一个经过修改的解决方案:

    -- new test data
    INSERT INTO contact (id, name) VALUES (8, 'Jane');
    INSERT INTO contact (id, name) VALUES (9, 'Jane');
    
    SELECT c1_id, name
      FROM (
        SELECT c1.id AS c1_id, c1.name, c2.id AS c2_id, COUNT(1) AS cnt
          FROM contact c1
            JOIN contact c2 ON (c2.id != c1.id AND c2.name = c1.name)
            LEFT JOIN phone_number pn ON (pn.contact_id = c1.id)
        WHERE pn.contact_id IS NULL
          OR EXISTS (SELECT 1
                    FROM phone_number
                  WHERE contact_id = c2.id
                    AND phone = pn.phone)
        GROUP BY c1.id, c1.name, c2.id
      )
    WHERE (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id) IN (0, cnt)
      AND (SELECT COUNT(1) FROM phone_number WHERE contact_id = c1_id) = (SELECT COUNT(1) FROM phone_number WHERE contact_id = c2_id)
    ;
    

    我们允许没有电话号码(左连接)的情况,在外部查询中,我们现在比较用户的电话号码-它必须等于0,或者是从内部查询返回的号码。

    在我的问题中,我创建了一个大大简化的模式,反映了我正在解决的实际问题。Przemyslaw的答案确实是正确的,并且对示例模式和真实模式都做了我所要求的

    但是,在使用真实模式和更大的(~10k条记录)数据集进行了一些实验之后,我发现性能是一个问题。我并不自称是索引大师,但我无法找到比模式中已有的更好的索引组合

    因此,我提出了一个替代解决方案,它可以满足相同的需求,但执行时间很短(<10%),至少使用SQLite3—我的生产引擎。希望它能帮助其他人,我将提供它作为我问题的另一个答案

    DROP TABLE IF EXISTS Contact;
    DROP TABLE IF EXISTS PhoneNumber;
    
    CREATE TABLE Contact (
      Id    INTEGER PRIMARY KEY,
      Name  TEXT
    );
    
    CREATE TABLE PhoneNumber (
      Id          INTEGER PRIMARY KEY,
      Contact_Id  INTEGER REFERENCES Contact (Id) ON UPDATE CASCADE ON DELETE CASCADE,
      Number      TEXT
    );
    
    INSERT INTO Contact (Id, Name) VALUES
      (1, 'John Smith'),
      (2, 'John Smith'),
      (3, 'John Smith'),
      (4, 'Jane Smith'),
      (5, 'Bob Smith'),
      (6, 'Bob Smith');
    
    INSERT INTO PhoneNumber (Id, Contact_Id, Number) VALUES
      (1, 1, '555-1212'),
      (2, 1, '222-1515'),
      (3, 2, '222-1515'),
      (4, 2, '555-1212'),
      (5, 3, '111-2525'),
      (6, 4, '111-2525');
    
    COMMIT;
    
    SELECT *
    FROM Contact c1
    WHERE EXISTS (
      SELECT 1
      FROM Contact c2
      WHERE c2.Id > c1.Id
        AND c2.Name = c1.Name
        AND (SELECT COUNT(*) FROM PhoneNumber WHERE Contact_Id = c2.Id) = (SELECT COUNT(*) FROM PhoneNumber WHERE Contact_Id = c1.Id)
        AND (
          SELECT COUNT(*)
          FROM PhoneNumber p1
          WHERE p1.Contact_Id = c2.Id
            AND EXISTS (
              SELECT 1
              FROM PhoneNumber p2
              WHERE p2.Contact_Id = c1.Id
                AND p2.Number = p1.Number
            )
        ) = (SELECT COUNT(*) FROM PhoneNumber WHERE Contact_Id = c1.Id)
    )
    ;
    
    结果如预期:

    Id     Name
    ====== =============
    1      John Smith
    5      Bob Smith
    

    其他发动机必然具有不同的性能,这可能是完全可以接受的。对于这个模式,这个解决方案似乎与SQLite配合得很好。

    在我的问题中,我创建了一个非常简化的模式,它反映了我正在解决的实际问题。Przemyslaw的答案确实是正确的,并且对示例模式和真实模式都做了我所要求的

    但是,在使用真实模式和更大的(~10k条记录)数据集进行了一些实验之后,我发现性能是一个问题。我并不自称是索引大师,但我无法找到比模式中已有的更好的索引组合

    因此,我提出了一个替代解决方案,它可以满足相同的需求,但执行时间很短(<10%),至少使用SQLite3—我的生产引擎。希望它能帮助其他人,我将提供它作为我问题的另一个答案

    DROP TABLE IF EXISTS Contact;
    DROP TABLE IF EXISTS PhoneNumber;
    
    CREATE TABLE Contact (
      Id    INTEGER PRIMARY KEY,
      Name  TEXT
    );
    
    CREATE TABLE PhoneNumber (
      Id          INTEGER PRIMARY KEY,
      Contact_Id  INTEGER REFERENCES Contact (Id) ON UPDATE CASCADE ON DELETE CASCADE,
      Number      TEXT
    );
    
    INSERT INTO Contact (Id, Name) VALUES
      (1, 'John Smith'),
      (2, 'John Smith'),
      (3, 'John Smith'),
      (4, 'Jane Smith'),
      (5, 'Bob Smith'),
      (6, 'Bob Smith');
    
    INSERT INTO PhoneNumber (Id, Contact_Id, Number) VALUES
      (1, 1, '555-1212'),
      (2, 1, '222-1515'),
      (3, 2, '222-1515'),
      (4, 2, '555-1212'),
      (5, 3, '111-2525'),
      (6, 4, '111-2525');
    
    COMMIT;
    
    SELECT *
    FROM Contact c1
    WHERE EXISTS (
      SELECT 1
      FROM Contact c2
      WHERE c2.Id > c1.Id
        AND c2.Name = c1.Name
        AND (SELECT COUNT(*) FROM PhoneNumber WHERE Contact_Id = c2.Id) = (SELECT COUNT(*) FROM PhoneNumber WHERE Contact_Id = c1.Id)
        AND (
          SELECT COUNT(*)
          FROM PhoneNumber p1
          WHERE p1.Contact_Id = c2.Id
            AND EXISTS (
              SELECT 1
              FROM PhoneNumber p2
              WHERE p2.Contact_Id = c1.Id
                AND p2.Number = p1.Number
            )
        ) = (SELECT COUNT(*) FROM PhoneNumber WHERE Contact_Id = c1.Id)
    )
    ;
    
    结果如预期:

    Id     Name
    ====== =============
    1      John Smith
    5      Bob Smith
    

    其他发动机必然具有不同的性能,这可能是完全可以接受的。对于这个模式,这个解决方案似乎与SQLite配合得很好。

    您需要连接两个表,按名称分组,按编号和
    count(Id)
    ,然后使用
    HAVING
    子句来获得
    count(Id)>1
    但是
    (PhoneNumber.Contact\u Id,PhoneNumber.number)
    上应该有一个唯一的约束。否则,您将面临为同一联系人ID多次存储相同号码的风险(顺便说一句,这可能会使在导入大型数据集时确定重复数据集变得更加困难)。Andrey的评论是一个不错的评论。但是,如果实用程序希望预先以最少的验证接收数据,然后再进行清理,那么最好创建一个