Как удалить дубликаты в MySQL с помощью Rails?

#mysql #sql #ruby-on-rails

#mysql #sql #ruby-on-rails

Вопрос:

У меня есть следующая таблица:

Отношения

 [id,user_id,status]
1,2,sent_reply
1,2,sent_mention
1,3,sent_mention
1,4,sent_reply
1,4,sent_mention
  

Я ищу способ удалить дубликаты, чтобы остались только следующие строки:

 1,2,sent_reply
1,3,sent_mention
1,4,sent_reply
  

(Предпочтительно с использованием Rails)

Комментарии:

1. Вы хотите возвращать только элементы uniq или удалить все дубликаты?

2. также вы получили одинаковый идентификатор для всех ваших отношений

3. Итак, вам нужна только одна пара (id, user_id), независимо от статуса? Как вы решаете, какое сообщение о состоянии сохранить? Последний записанный? Первый? Случайно?

4. Я хочу удалить дубликаты, судя по первым двум полям (id, user_id). Мой пример немного вводит в заблуждение, поскольку id — это не первичный ключ (который был бы уникальным), а какой-то другой идентификатор (думайте об этом как member_id)

5. @marc-b хороший момент, я хочу сохранить записи «отправленный ответ»

Ответ №1:

Я знаю, что это слишком поздно, но я нашел хороший способ сделать это с помощью Rails 3. Однако, вероятно, есть способы получше, и я не знаю, как это будет работать с более чем 100 000 строками данных, но это должно вывести вас на правильный путь.

 # Get a hash of all id/user_id pairs and how many records of each pair
counts = ModelName.group([:id, :user_id]).count
# => {[1, 2]=>2, [1, 3]=>1, [1, 4]=>2}

# Keep only those pairs that have more than one record
dupes = counts.select{|attrs, count| count > 1}
# => {[1, 2]=>2, [1, 4]=>2}

# Map objects by the attributes we have
object_groups = dupes.map do |attrs, count|
  ModelName.where(:id => attrs[0], :user_id => attrs[1])
end

# Take each group and #destroy the records you want.
# Or call #delete instead to save time if you don't need ActiveRecord callbacks
# Here I'm just keeping the first one I find.
object_groups.each do |group|
  group.each_with_index do |object, index|
    object.destroy unless index == 0
  end
end
  

Ответ №2:

Лучше сделать это через SQL. Но если вы предпочитаете использовать Rails:

 (Relation.all - Relation.all.uniq_by{|r| [r.user_id, r.status]}).each{ |d| d.destroy }
  

или

  ids = Relation.all.uniq_by{|r| [r.user_id, r.status]}.map(amp;:id)
 Relation.where("id IS NOT IN (?)", ids).destroy_all # or delete_all, which is faster
  

Но мне не нравится это решение: D

Комментарии:

1. Это было бы очень медленно и потребляло бы много памяти (моя таблица отношений насчитывает более 100 000 строк. Есть ли еще какой-нибудь SQLish-способ сделать это. В этот момент не очень важно обернуть это в rails.