【问题标题】:Efficient checking of possible duplicate entities有效检查可能的重复实体
【发布时间】:2010-10-23 23:08:14
【问题描述】:

我需要在用户将实体保存到数据库之前生成可能重复的列表并警告他们可能的重复。

我们应该根据 7 个标准检查重复项,如果至少 3 个匹配,我们应该将其标记给用户。 条件将在 ID 上全部匹配,因此不需要模糊字符串匹配,但我的问题来自这样一个事实,即至少有 3 个项目可以匹配来自7 种可能的列表。

我不想执行 99 个单独的数据库查询来查找我的搜索结果,也不想从数据库中取回全部内容并在客户端进行过滤。目前我们可能只是在谈论几万条记录,但随着系统的成熟,这将增长到数百万条。

有人想到了一种很好的有效方法吗? 我正在考虑一个简单的 OR 查询以从数据库中获取至少一个字段匹配的记录,然后在客户端上进行一些处理以对其进行更多过滤,但是其中一些字段的基数非常低,实际上不会减少数量巨大。

谢谢 乔恩

【问题讨论】:

    标签: database search duplicate-data


    【解决方案1】:

    ORCASE 求和会起作用,但效率很低,因为它们不使用索引。

    您需要设置UNION 才能使索引可用。

    如果用户将namephoneemailaddress 输入到数据库中,并且您想检查至少与这些字段中的3 匹配的所有记录,则发出:

    SELECT  i.*
    FROM    (
            SELECT  id, COUNT(*)
            FROM    (
                    SELECT  id
                    FROM    t_info t
                    WHERE   name  = 'Eve Chianese'
                    UNION ALL
                    SELECT  id
                    FROM    t_info t
                    WHERE   phone = '+15558000042'
                    UNION ALL
                    SELECT  id
                    FROM    t_info t
                    WHERE   email = '42@example.com'
                    UNION ALL
                    SELECT  id
                    FROM    t_info t
                    WHERE   address = '42 North Lane'
                    ) q
            GROUP BY
                    id
            HAVING  COUNT(*) >= 3
            ) dq
    JOIN    t_info i
    ON      i.id = dq.id
    

    这将在这些字段上使用索引并且查询会很快。

    详情请看我博客中的这篇文章:

    另请参阅本文所基于的question

    如果您想在现有数据中包含DISTINCT 值的列表,只需将此查询包装到子查询中:

    SELECT  i.*
    FROM    t_info i1
    WHERE   EXISTS
            (
            SELECT  1
            FROM    (
                    SELECT  id
                    FROM    t_info t
                    WHERE   name  = i1.name
                    UNION ALL
                    SELECT  id
                    FROM    t_info t
                    WHERE   phone = i1.phone
                    UNION ALL
                    SELECT  id
                    FROM    t_info t
                    WHERE   email = i1.email
                    UNION ALL
                    SELECT  id
                    FROM    t_info t
                    WHERE   address = i1.address
                    ) q
            GROUP BY
                    id
            HAVING  COUNT(*) >= 3
            )
    

    请注意,DISTINCT 不具有传递性:如果 A 匹配 B 并且 B 匹配 C,这并不意味着 A 匹配 C

    【讨论】:

    • 谢谢,认为这个看起来是解决我问题的最佳方案。我们仍然需要在一个包含许多元素的集合上进行分组,但是做一些测试似乎比我尝试过的其他方法要快。
    【解决方案2】:

    您可能需要以下内容:

    SELECT id
    FROM 
        (select id, CASE fld1 WHEN input1 THEN 1 ELSE 0 "rule1",
            CASE fld2 when input2 THEN 1 ELSE 0 "rule2",
            ...,
            CASE fld7 when input7 THEN 1 ELSE 0 "rule2",
        FROM table)
    WHERE rule1+rule2+rule3+...+rule4 >= 3
    

    这未经测试,但它显示了解决此问题的方法。

    【讨论】:

      【解决方案3】:

      您使用的是什么星展银行?一些支持通过使用服务器端代码来使用此类约束。

      【讨论】:

        【解决方案4】:

        您是否考虑过使用带有游标的存储过程?然后,您可以执行 OR 查询,然后逐个遍历记录以查找匹配项。使用存储过程将允许您在服务器上进行所有检查。

        但是,我认为包含数百万条记录的表扫描总是很慢。我认为你应该找出最有可能匹配的 7 个字段中的哪一个,并确保它们被编入索引。

        【讨论】:

          【解决方案5】:

          我假设您的系统正在尝试匹配某个帖子或类似内容的标签 ID。这是一个多对多的关系,你应该有三个表来处理它。一个用于帖子,一个用于标签,一个用于帖子和标签的关系。

          如果我的假设是正确的,那么最好的处理方法是:

          SELECT postid, count(tagid) as common_tag_count
          FROM posts_to_tags
          WHERE tagid IN (tag1, tag2, tag3, ...)
          GROUP BY postid
          HAVING count(tagid) > 3;
          

          【讨论】:

            猜你喜欢
            • 2015-02-23
            • 1970-01-01
            • 2013-09-21
            • 2016-07-26
            • 2010-09-30
            • 1970-01-01
            • 2014-02-02
            • 2014-08-17
            • 2012-03-25
            相关资源
            最近更新 更多