SQL 查询查找准确和接近的重复项

SQL Query Find Exact and Near Dupes

我有一个 SQL table,其中包含 FirstName、LastName、Add1 和其他字段。我正在努力清理这些数据。有几个可能被骗的例子 -

  1. 超过 1 条记录的所有 3 列完全相同
  2. First和Last一样,只有1个有地址,另一个是空白
  3. 第一个和最后一个相似(John | Doe vs John C. | Doe)并且地址相同或一个为空

我想生成一个查询,我可以提供给用户,这样他们就可以检查这些记录,比较他们的相关记录,然后删除他们不需要的。

我一直在研究相似度函数,soundex,等等,但是看起来太复杂了。有没有简单的方法可以做到这一点?

谢谢!


编辑:

下面是一些示例数据:

FirstName    |   LastName    |      Add1
John         |   Doe         |   1 Main St
John         |   Doe         |   
John A.      |   Doe         |   
Jane         |   Doe         |   2 Union Ave
Jane B.      |   Doe         |   2 Union Ave
Alex         |   Smith       |   3 Broad St
Chris        |   Anderson    |   4 South Blvd
Chris        |   Anderson    |   4 South Blvd

我非常喜欢 Critical Error 用于识别所有不同类型的骗子的查询。这将给我上面的样本数据,不包括亚历克斯·史密斯的结果,因为没有骗子。

我想做的是获取该结果集并确定哪些是 Jane Doe 的骗子。她应该只有两个骗子。 John Doe 有 3 个,Chris Anderson 有 2 个。我可以得到那个子结果集吗?


编辑:

我想通了!我会将 Critical Error 的答案标记为解决方案,因为它完全让我到达了我需要去的地方。这是解决方案,以防它可能对其他人有所帮助。基本上,这就是我们正在做的。

  1. 从table中选择有重复的记录
  2. 添加 WHERE EXISTS 子查询以在相同的 table 中查找完全相同的重复项,其中主查询和子查询的 ID 不匹配
  3. 添加 WHERE EXISTS 子查询以在相同的 table 中查找类似的重复项,使用重复列之间的差异因子,其中主查询和子查询的 ID 不匹配
  4. 添加 WHERE EXISTS 子查询以在相同的 table 中查找 2 个字段上的重复项,其中第 3 个字段对于其中一个记录可能为空,其中来自主查询和子查询的 ID不匹配
  5. 每个子查询都与一个 OR 连接,因此可以找到任何类型的重复项
  6. 在每个子查询的末尾添加一个嵌套要求,即主查询或子查询是您要为其识别重复项的记录的 ID。
    DECLARE @CID AS INT
    
    SET ANSI_NULLS ON
    
        SET NOCOUNT ON;
        SET @CID = 12345
    BEGIN
    
    
    SELECT
        *
    FROM @Customers c
    WHERE
        -- Exact duplicates.
        EXISTS (
    
            SELECT * FROM @Customers x WHERE
                x.FirstName = c.FirstName 
                AND x.LastName = c.LastName 
                AND x.Add1 = c.Add1 
                AND x.Id <> c.Id
                AND (x.ID = @CID OR c.ID = @CID)
        
        )
        -- Match First/Last name are same/similar and the address is same.
        OR EXISTS (
        
            SELECT * FROM @Customers x WHERE
                DIFFERENCE( x.FirstName, c.FirstName ) = 4 
                AND DIFFERENCE( x.LastName, c.LastName ) = 4
                AND x.Add1 = c.Add1
                AND x.Id <> c.Id
                AND (x.ID = @CID OR c.ID = @CID)
    
        )
        -- Match First/Last name and one address exists.
        OR EXISTS (
        
            SELECT * FROM @Customers x WHERE
                x.FirstName = c.FirstName 
                AND x.LastName = c.LastName
                AND x.Id <> c.Id
                AND (
                    x.Add1 IS NULL AND c.Add1 IS NOT NULL
                    OR
                    x.Add1 IS NOT NULL AND c.Add1 IS NULL
                )
                AND (x.ID = @CID OR c.ID = @CID)
        );

假设你在记录之间有一个唯一的 id,你可以试一试:

DECLARE @Customers table ( FirstName varchar(50), LastName varchar(50), Add1 varchar(50), Id int IDENTITY(1,1) );
INSERT INTO @Customers ( FirstName, LastName, Add1 ) VALUES
    ( 'John', 'Doe', '123 Anywhere Ln' ),
    ( 'John', 'Doe', '123 Anywhere Ln' ),
    ( 'John', 'Doe', NULL ),
    ( 'John C.', 'Doe', '123 Anywhere Ln' ),
    ( 'John C.', 'Doe', '15673 SW Liar Dr' );

SELECT
    *
FROM @Customers c
WHERE
    -- Exact duplicates.
    EXISTS (

        SELECT * FROM @Customers x WHERE
            x.FirstName = c.FirstName 
            AND x.LastName = c.LastName 
            AND x.Add1 = c.Add1 
            AND x.Id <> c.Id
    
    )
    -- Match First/Last name are same/similar and the address is same.
    OR EXISTS (
    
        SELECT * FROM @Customers x WHERE
            DIFFERENCE( x.FirstName, c.FirstName ) = 4 
            AND DIFFERENCE( x.LastName, c.LastName ) = 4
            AND x.Add1 = c.Add1
            AND x.Id <> c.Id

    )
    -- Match First/Last name and one address exists.
    OR EXISTS (
    
        SELECT * FROM @Customers x WHERE
            x.FirstName = c.FirstName 
            AND x.LastName = c.LastName
            AND x.Id <> c.Id
            AND (
                x.Add1 IS NULL AND c.Add1 IS NOT NULL
                OR
                x.Add1 IS NOT NULL AND c.Add1 IS NULL
            )
    );

Returns

+-----------+----------+-----------------+----+
| FirstName | LastName |      Add1       | Id |
+-----------+----------+-----------------+----+
| John      | Doe      | 123 Anywhere Ln |  1 |
| John      | Doe      | 123 Anywhere Ln |  2 |
| John      | Doe      | NULL            |  3 |
| John C.   | Doe      | 123 Anywhere Ln |  4 |
+-----------+----------+-----------------+----+

初始结果集:

+-----------+----------+------------------+----+
| FirstName | LastName |       Add1       | Id |
+-----------+----------+------------------+----+
| John      | Doe      | 123 Anywhere Ln  |  1 |
| John      | Doe      | 123 Anywhere Ln  |  2 |
| John      | Doe      | NULL             |  3 |
| John C.   | Doe      | 123 Anywhere Ln  |  4 |
| John C.   | Doe      | 15673 SW Liar Dr |  5 |
+-----------+----------+------------------+----+