MYSQL PHP: найти дубликаты на основе столбца адресов - PullRequest
2 голосов
/ 09 июля 2020

У меня есть таблица адресов в моей базе данных MYSQL со следующей структурой:

  • ID первого столбца - это основной столбец с автоинкрементом.
  • Второй Имя столбца - varchar.
  • Третий столбец содержит адрес (текст), заполненный пользователем.
  • Четвертый столбец содержит слаг адреса, который в основном является адресом (третий столбец) в нижнем регистре и без специальных символов.
  • Последний столбец содержит дату создания записи.

введите описание изображения здесь

I wi sh, чтобы отобразить все записи и выделить возможные дубликаты на основе адреса / краткого названия адреса.

В этом случае дубликаты выглядят следующим образом:

  • Запись 1 и Запись 2
  • Запись 3 и Запись 6

Есть ли способ частично сопоставить строку в MYSQL или PHP, чтобы достичь вышеуказанных результатов?

К вашему сведению: я прошел через SPHINX PHP, SQL ПОЛНОТЕКСТОВЫЕ ПОИСКИ и c.

Я боролся более 2 недель, но не смог найти оптимального решения.

Любые идеи, предложения, решения приветствуются.

Ответы [ 2 ]

1 голос
/ 10 июля 2020
  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 покажет только одно встречающееся имя. Но это может также ничего не вернуть, если есть такие имена, как Joe и Joe 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;

С этим вы go над возвращенными значениями для любого из дубликатов и сравните их рядом. Таким образом, вы можете решить опустить любой идентификатор, который не должен отображаться в списке, добавив WHERE ID NOT IN(1,3 ...) et c.

  • После того, как вы окончательно определили, какой 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

1 голос
/ 09 июля 2020

Поскольку 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"
]

Теперь мы снова oop, чтобы сравнить возможное повторение. Здесь мы используем Laravel s 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 . '"';
                }
            }
        }
    }
}

Final Response of $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]"
]

Держите меня в курсе в комментариях ниже. Ура!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...