【问题标题】:SQL: Detect duplicate customersSQL:检测重复客户
【发布时间】:2012-02-22 03:35:37
【问题描述】:

我正在尝试创建一个 sql 查询,它将在我的数据库中检测(可能)重复的客户:

我有两张桌子:

  1. 客户 列:cid、名字、姓氏、zip。请注意,cid 是此表的唯一客户 ID 和主键。
  2. IgnoreForDuplicateCustomer 列:cid1、cid2。两列都是外键,引用了 Customer(cid)。这张表是用来说cid1的客户和cid2的客户不一样的。

例如,如果我有

  • cid = 1、firstname="foo"、lastname="anonymous" 和 zip="11231" 的客户条目
  • 以及另一个带有 cid=2、firstname="foo"、lastname="anonymous" 和 zip="11231" 的客户条目。

所以我的 sql 查询应该搜索具有相同名字、姓氏和 zip 的客户,并且检测到 cid = 1 的客户与 cid = 2 的客户相同。

但是,通过设置 cid1 = 1 和 cid2 = 2 在 IgnoreForDuplicateCustomer 表中存储一个新条目,应该可以说客户 cid = 1 和 cid=2 不同。

因此,使用此 sql 查询脚本可以很好地检测重复客户:

SELECT cid, firstname, lastname, zip, COUNT(*) AS NumOccurrences
       FROM Customer
 GROUP BY fistname, lastname,zip
       HAVING ( COUNT(*) > 1 )

我的问题是,我无法将 IgnoreForDuplicateCustomer 表集成到 就像在我之前的示例中一样,具有 cid = 1 和 cid=2 的客户不会被标记/查询为相同,因为 IgnoreForDuplicateCustomer 表中有一个条目/规则。

所以我尝试通过添加 where 子句来扩展我之前的查询:

    SELECT cid, firstname, lastname, COUNT(*) AS NumOccurrences
               FROM Customer    
    WHERE cid NOT IN (
                     SELECT cid1 FROM IgnoreForDuplicateCustomer WHERE cid2=cid 
                     UNION 
                     SELECT cid2 FROM IgnoreForDuplicateCustomer WHERE cid1=cid
                     )  
     GROUP BY firstname, lastname, zip
     HAVING ( COUNT(*) > 1 )

不幸的是,这个额外的 WHERE 子句对我的结果完全没有影响。 有什么建议吗?

【问题讨论】:

    标签: mysql sql ignore-duplicates


    【解决方案1】:

    你在这里:

    Select a.*
    From (
      select c1.cid 'CID1', c2.cid 'CID2'
      from Customer c1 
      join Customer c2 on c1.firstname=c2.firstname 
        and c1.lastname=c2.lastname and c1.zip=c2.zip
        and c1.cid < c2.cid) a
    Left Join (
      Select cid1 'CID1', cid2 'CID2'
      From ignoreforduplicatecustomer one
     Union
      Select cid2 'CID1', cid1 'CID2'
      From ignoreforduplicatecustomer two) b on a.cid1 = b.cid1 and a.cid2 = b.cid2
    where b.cid1 is null
    

    这将为您提供customer 表中重复记录的 ID,这些记录不在表 ignoreforduplicatecustomer 中。

    测试:

    CREATE TABLE IF NOT EXISTS `customer` (
     `CID` int(11) NOT NULL AUTO_INCREMENT,
     `Firstname` varchar(50) NOT NULL,
     `Lastname` varchar(50) NOT NULL,
     `ZIP` varchar(10) NOT NULL,
     PRIMARY KEY (`CID`)) 
    ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=100 ;
    
    INSERT INTO `customer` (`CID`, `Firstname`, `Lastname`, `ZIP`) VALUES
    (1, 'John', 'Smith', '1234'),
    (2, 'John', 'Smith', '1234'),
    (3, 'John', 'Smith', '1234'),
    (4, 'Jane', 'Doe', '1234');
    

    还有:

    CREATE TABLE IF NOT EXISTS `ignoreforduplicatecustomer` (
     `CID1` int(11) NOT NULL,
     `CID2` int(11) NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    
    
    INSERT INTO `ignoreforduplicatecustomer` (`CID1`, `CID2`) VALUES
    (1, 2);
    

    我的测试设置结果是:

    CID1  CID2
     1     3
     2     3
    

    【讨论】:

    • 您好,非常感谢!但是,我不明白为什么我在第一次加入时需要这个“and c1.cid
    • 好吧,您希望这些 ID 不同,因此您知道它不是同一行。你可以使用c1.cid != c2.cid,但这会让你获得太多的点击率,因为它将包括两个基本相同的情况(c1.cid &lt; c2.cidc1.cid &gt; c2.cid)。在我的示例中,您还将获得 3、1 和 3、2。通过使用 &lt;,每个重复项仅包含一次。
    【解决方案2】:

    根据 TPete 的评论进行编辑(不要尝试):

    SELECT 
        C1.cid, C1.firstname, C1.lastname
    FROM 
        Customer C1,
        Customer C2
    WHERE
        C1.cid < C2.cid AND 
        C1.firstname = C2.firstname AND 
        C1.lastname = C2.lastname AND 
        C1.zip = C2.zip AND 
        CAST(C1.cid AS VARCHAR)+' ' +CAST(C2.cid AS VARCHAR) <> 
           (SELECT CAST(cid1 AS VARCHAR)+' '+CAST(cid2 AS VARCHAR) FROM IgnoreForDuplicateCustomer I WHERE I.cid1 = C1.cid AND I.cid2 = C2.cid);
    

    最初我认为IgnoreForDuplicateCustomer 是客户表中的一个字段。

    【讨论】:

    • IgnoreForDuplicateCustomer 是另一个表的名称,所以这不起作用。
    • 嗨,我正在使用 mysql 并收到 cid、名字、姓氏不明确的错误,我不明白为什么我应该使用这个条件 C1.cid
    • 列名需要限定,用户C1C2。条件应该是C1.cid != C2.cid,尽管在这种情况下结果是一样的。
    • 已编辑以限定字段名称,感谢@TPete。我认为 != 会选择两条记录(小于和大于都符合条件),所以我的 C1.cid &lt; C2.cid 应该是对的。
    • 谢谢,我不知道为什么,但在附近出现语法错误:VARCHAR)+' ' +CAST(C2.cid AS VARCHAR) (SELECT CAST(cid1 AS VARCHAR)+
    【解决方案3】:

    很疯狂,但我认为它有效 :)

    首先我在名称上加入客户表以获取重复项 然后我排除了 IgnoreForDuplicateCustomer 表上的键(联合是因为第一个查询返回 cid1、cid2 和 cid2、cid1

    结果会重复,但我认为您可以获得所需的信息

    select c1.cid, c2.cid
    from Customer c1 
         join Customer c2 on c1.firstname=c2.firstname 
         and c1.lastname=c2.lastname and c1.zip=c2.zip
         and c1.cid!=c2.cid
    except 
    (
        select cid1,cid2 from IgnoreForDuplicateCustomer
        UNION
        select cid2,cid1 from IgnoreForDuplicateCustomer
    )
    

    第二枪:

    select firstname,lastname,zip from Customer 
    group by firstname,lastname,zip 
    having (count(*)>1)
    except
    select c1.firstname, c1.lastname, c1.zip
    from Customer c1 join IgnoreForDuplicateCustomer IG on c1.cid=ig.cid1 join Customer c2 on ig.cid2=c2.cid
    

    第三个:

    select firstname,lastname,zip from (
        select firstname,lastname,zip from Customer 
        group by firstname,lastname,zip 
        having (count(*)>1)
    ) X
    where firstname not in (
    select c1.firstname
    from Customer c1 join IgnoreForDuplicateCustomer IG on c1.cid=ig.cid1 join Customer c2 on ig.cid2=c2.cid
    )
    

    【讨论】:

    • 不幸的是,我使用 mysql 并且 mysql 不支持 except ...也许 mysql NOT EXISTS 可以提供帮助,但我不确定,如果有更多的两个客户相同,你的 sql 代码是否可以工作名字、姓氏和邮编...
    • 看起来不错,除了except。我猜你可以用Left Join 替换它,并且只保留不匹配的联合行。
    • 没有考虑到这一点。是的(我测试过),但结果太乱了:(
    • 嗨,我添加了第二个查询。试试NOT EXISTS
    • 嗨,我用 NOT EXISTS 尝试过:从客户那里选择名字、姓氏、zip、COUNT(*) AS nr,但不存在(从客户那里选择 c1.firstname、c1.lastname、c1.zip c1 在 c1.cid=ig.cid1 上加入 IgnoreForDuplicateCustomer ig1 在 ig.cid2=c2.cid) 上加入客户 c2 按名字、姓氏、zip 分组 (count(*)>1) 在 IgnoreForDuplicateCustomer 中没有条目时效果很好,但是如果我使用 INSERT INTO IgnoreForDuplicateCustomer VALUES (1,2) 添加一个新的 IgnoreForDuplicateCustomer 条目,它将始终返回一个空的结果集 ...
    猜你喜欢
    • 2012-11-22
    • 2020-01-24
    • 1970-01-01
    • 2016-08-10
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多