Удалить дубликаты записей из большой коллекции Laravel - PullRequest
1 голос
/ 21 октября 2019

У меня есть большая таблица базы данных (~ 1 миллион записей), которая мне нужна для очистки дубликатов записей. Структура таблицы следующая:

|----|-------------|-----|-----|--------------------|
| id | relation_id | foo | bar | timestamp          |
|----|-------------|-----|-----|--------------------|
| 1  | 1           |14.20|0.22 |2019-10-21 14:00:01 |
| 2  | 1           |14.20|0.22 |2019-10-21 14:00:01 |
| 3  | 1           |14.20|0.22 |2019-10-21 14:00:01 |
| 4  | 2           |10.36|0.75 |2019-10-21 14:00:01 |
| 5  | 2           |10.36|0.75 |2019-10-21 14:00:01 |
| 6  | 2           |10.36|0.75 |2019-10-21 14:00:01 |
|----|-------------|-----|-----|--------------------|

В соответствии с приведенным выше примером существует много записей, которые имеют одинаковую комбинацию значений relation_id, foo, bar и * 1007. *. Мне нужно создать сценарий, который будет запускаться для определения уникальных значений, а затем удалять и дублировать ссылки. Поэтому я хотел бы получить что-то вроде:

|----|-------------|-----|-----|--------------------|
| id | relation_id | foo | bar | timestamp          |
|----|-------------|-----|-----|--------------------|
| 1  | 1           |14.20|0.22 |2019-10-21 14:00:01 |
| 4  | 2           |10.36|0.75 |2019-10-21 14:00:01 |
|----|-------------|-----|-----|--------------------|

Я протестировал циклический просмотрrelation_id (поскольку существует только 20 уникальных значений), а затем выполнить что-то вроде этого, чтобы создать коллекцию уникальных записей:

$unique     = collect([]);
$collection = Model::where('relation_id', $relation_id)->chunk(100, function($items) use ($unique) {
    $unique->push($items->unique()->values()->all());
});

После этого я планировал выполнить цикл по всей модели. записи и удалить, если элемент не был в коллекции $unique. Примерно так:

Model::chunk(100, function($items) {
    foreach ($items as $item) {
        if(!$unique->contains('id', $item->id)){
            $item->delete;
        }
    }
});

Моя проблема в том, что таблица базы данных настолько велика, что я не могу проверить, работает ли эта логика. Выполнение первой части вышеописанного сценария (для заполнения $unique) для одного $relation_id пробежало в течение 30 минут без результатов.

Я относительно уверен, что это не лучший подход кудаляйте дублирующиеся записи, так как мой подход требует нескольких запросов, которые, как я полагаю, могут быть оптимизированы (что крайне важно при работе с такой большой таблицей).

Итак, каков наиболее эффективный способ запроса таблицы базы данных? проверить уникальные записи (на основе нескольких столбцов) и удалить дублирующиеся записи?

Ответы [ 2 ]

1 голос
/ 23 октября 2019

Для тех, кто сталкивается с подобной проблемой здесь, я решил использовать MySQL для обработки базы данных, так как это было намного эффективнее, чем загрузка в память с помощью Eloquent.

Это сценарий, который я использовал:

DELETE table_name FROM table_name LEFT OUTER JOIN (
SELECT MIN(ID) AS minID FROM table_name GROUP BY table_name.relation_id, table_name.timestamp
) AS keepRowTable ON table_name.ID = keepRowTable.minID
WHERE keepRowTable.minID IS NULL

Я принял ответ @ Vince, потому что его подход работает с использованием Laravel, но мы столкнулись с проблемами при попытке обработать такой большой набор данных. Кроме того, он герой за отзывчивость в комментариях!

1 голос
/ 21 октября 2019

Вы можете разрешить базе данных здесь подниматься. Вы можете запросить базу данных, используя GROUP BY, а затем удалить все, что не соответствует вашему запросу.

$ids = Model::groupBy(['relation_id', 'foo', 'bar', 'timestamp'])
            ->get(['id'])
            ->all();

Это переводит в следующий SQL:

SELECT id FROM models GROUP BY relation_id, foo, bar, timestamp;

Так что теперь $ids - это массив идентификаторов, где другие столбцы уникальны ([1, 4]). Таким образом, вы можете выполнить следующее, чтобы удалить все другие строки из БД:

Model::whereNotIn('id', $ids)->delete();

Однако, поскольку $ids, вероятно, огромный , вы, вероятно,поразить некоторые ограничения верхнего предела. В этом случае вы можете попробовать использовать array_chunk() для добавления нескольких предложений whereNotIn к запросу:

$query = Model::query();

foreach(array_chunk($ids, 500) as $chunk) {
  $query->whereNotIn('id', $chunk);
}

$query->delete();

Я создал SQL Fiddle , где выможете проверить это.

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