MYSQL PHP:根据“地址”列查找重复项

时间:2020-07-09 05:39:01

标签: php mysql duplicates

我的MYSQL数据库中有一个地址表,其结构如下:

  • 第一列ID是自动递增的主列。
  • 第二列名称是varchar。
  • 第三列包含用户填写的地址(文本)。
  • 第四列包含地址段,基本上是小写的地址(第三列),没有任何特殊字符。
  • 最后一列包含记录的创建日期。

enter image description here

我希望根据地址/地址段显示所有记录并突出显示可能的重复项。

在这种情况下,重复项如下:

  • 记录1和记录2
  • 记录3和记录6

有没有一种方法可以部分匹配MYSQL或PHP中的字符串,以实现上述结果?

仅供参考:我已经完成了SPHINX PHP,SQL FULLTEXT SEARCHES等工作。

我已经苦苦挣扎了2个多星期,但找不到最佳解决方案。

欢迎任何想法,建议和解决方案。

2 个答案:

答案 0 :(得分:1)

由于laravel最初是被标记的,后来被删除了,所以我认为该策略仍然可以提供帮助。

这是给定的列表:

$lists = [
    [
        'id' => 1,
        'text' => '2693 Edgewood Road Exit',
    ],
    [
        'id' => 2,
        'text' => '4408 Cost 4657 Avenue',
    ],
    [
        'id' => 3,
        'text' => '2693 Mapleview Road',
    ],
    [
        'id' => 4,
        'text' => '4657 Cost Edgewood Avenue',
    ],
    [
        'id' => 5,
        'text' => '4408 Mapleview Drive Road',
    ]
];

目标是从每个文本中查找重复/重复的文本。


由于发现一个单词的重复不是一个真实的情况,所以我想到了以两个单词以及所有可能的组合来查找重复的单词。

    $combinations = [];
    foreach ($lists as $list) {

        $insideCombo = [];
        $insideText = explode(' ', $list['text']);
        $length = count($insideText);

        for ($i = 0; $i < $length; $i++) {
            for ($j = $i + 1; $j < $length; $j++) {
                if (isset($insideText[$j])) {
                    $insideCombo[] = $insideText[$i] . ' ' . $insideText[$j];
                }
            }
        }

        $combinations[$list['id']] = $insideCombo;
    }

这会回来的

// for '2693 Edgewood Road Exit'
1 => array:6 [
    0 => "2693 Edgewood"
    1 => "2693 Road"
    2 => "2693 Exit"
    3 => "Edgewood Road"
    4 => "Edgewood Exit"
    5 => "Road Exit"
]

现在,我们再次循环比较可能的重复。在这里,我们利用Laravel的Str::containsAll()

$copyCat = [];
foreach ($lists as $list) {
    foreach ($combinations as $comboKey => $combination) {
        /* no need to compare the text with itself && 
        *  to avoid duplication of '4 to 2' if '2 to 4' is already mentioned
        */
        if ($list['id'] != $comboKey && $list['id'] < $comboKey) {
            foreach ($combination as $row) {
                if (Str::containsAll($list['text'], explode(' ', $row))) {
                    $copyCat[] = $list['id'] . ' matches with ' . $comboKey . ' with "' . $row . '"';
                }
            }
        }
    }
}

最终答复$copyCat

array:5 [
  0 => "1 matches with 3 with [2693 Road]"
  1 => "2 matches with 4 with [4657 Cost]"
  2 => "2 matches with 4 with [4657 Avenue]"
  3 => "2 matches with 4 with [Cost Avenue]"
  4 => "3 matches with 5 with [Mapleview Road]"
]

将我保留在下面的评论中。干杯!

答案 1 :(得分:1)

  1. 对表进行空复制-例如mytable_to_update
  2. 运行一些查询以查找重复项。
  • 首先使用非重复项填充新创建的表。初始查询:
SELECT SUBSTRING_INDEX(Name,' ',1),COUNT(*) 
FROM mytable_to_update 
GROUP BY SUBSTRING_INDEX(Name,' ',1) HAVING COUNT(*) = 1;

SUBSTRING_INDEX将捕获空格('')之前的第一个字符串。在此示例中,Sam Mcarthy仅会变成Sam。然后使用该数据分组并计算出现的名称数量。 HAVING COUNT(*) = 1仅显示一次出现的任何名称。但是,如果有JoeJoe John之类的名字,但实际上这两个人实际上是具有不同地址的另一个人,则这可能不会返回任何内容(因为第一个查询仅按名字进行分组)。因此,我们需要在组合中添加address比较。

  • 像这样在Address列中添加相同的功能:
SELECT SUBSTRING_INDEX(Name,' ',1), 
       SUBSTRING_INDEX(Address,' ',1), /*we take the first string in the address*/
       COUNT(*)
FROM mytable_to_update 
GROUP BY SUBSTRING_INDEX(Name,' ',1), 
      SUBSTRING_INDEX(Address,' ',1) /*then add group by for the address*/
HAVING COUNT(*) = 1;

同样,我们仅从地址中获取第一个字符串。假设有两个看起来像这样的数据Joe, 12 Street..Joe John, 12 St. ..,将发生的是上面的查询(根据SUBSTRING_INDEX函数)仅出现第一个字符串; Joe, 12,将返回计数值为2。这意味着数据(Joe, 12 Street..Joe John, 12 St. ..)都被视为重复项,并且不会显示在查询结果中。

  • 更改查询以列出要插入到ID表中的所有非重复项mytable_to_update
INSERT INTO mytable_to_update 
SELECT * FROM mytable WHERE ID IN
(SELECT GROUP_CONCAT(ID) /*replace everything else in the select with just `ID`*/
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1), 
      SUBSTRING_INDEX(Address,' ',1)
HAVING COUNT(*) = 1) ;

注意:我正在使用GROUP_CONCAT(ID),因为sql_mode = only_full_group_by不兼容-如果正在设置它。当然结果可能会有所不同(例如'1,2'或'1 ,,,,'),但是由于我们只看任何count = 1,因此它不会有问题,因为它只会返回1值。我已经用ANY_VALUE测试过,它也会返回类似的结果。

现在,您在mytable_to_update表中拥有所有非重复项。下一步是搜索重复项,然后插入您只需要的重复项。这只是您可能想要的建议/假设,由于我们正在比较的数据值的性质,它并不是100%准确。

  • 查询的结构类似,仅在几个地方进行了更改,例如:
SELECT GROUP_CONCAT(ID), /*add GROUP_CONCAT to list all the duplicates group by the first name & address string.*/
       Name, 
       Address, 
       COUNT(*) 
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1), 
         SUBSTRING_INDEX(Address,' ',1) 
HAVING COUNT(*) > 1; /*Change '= 1' to '> 1' to get any records with more than 1 count.*/

使用GROUP_CONCAT生成以逗号分隔的ID列表,该列表可能重复。

  • 然后将GROUP_CONCAT添加到所有列有相同ORDER BY的列上,以便每个列都按相同的顺序排序。
SELECT GROUP_CONCAT(ID ORDER BY ID), /*add ORDER BY*/
       GROUP_CONCAT(Name ORDER BY ID), 
       GROUP_CONCAT(Address ORDER BY ID), 
       COUNT(*) 
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1), 
         SUBSTRING_INDEX(Address,' ',1) 
HAVING COUNT(*) > 1;

通过此操作,您可以遍历返回的所有重复值,并进行比较。这样,您可以通过添加WHERE ID NOT IN(1,3 ...)等来决定省略不想显示在列表中的任何ID。

  • 完成确定要保留的ID后,您可以执行以下操作:
INSERT INTO mytable_to_update 
SELECT * FROM mytable WHERE ID IN
(SELECT SUBSTRING_INDEX(GROUP_CONCAT(ID ORDER BY ID),',',1) 
     /*assuming that you only want the first ID in the set, do SUBSTRING_INDEX to separate the first ID*/
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1), 
         SUBSTRING_INDEX(Address,' ',1) 
HAVING COUNT(*) > 1);

现在,您将有一个表(mytable_to_update),该表可能包含所有非重复项。如果mytable_to_update中的某些数据不是您想要的,则可以将其删除,或者如果您认为某些数据不是重复的,则可以将其插入。此后几乎是一个手动过程。好吧,即使有查询,也只有您自己才能确定过程/数据是否正确。

这是一个小提琴:https://www.db-fiddle.com/f/6Dfrn78mqZbGTwZs3U9Vhi/0