ActiveRecord: удалить повторяющиеся записи

Если на определенную дату есть несколько записей, я хочу удалить все, кроме последней записи в этот день. Например, в таблице ниже записи с идентификаторами 9, 10, 12 имеют одинаковую дату. Таким образом, 9 и 10 должны быть удалены, поскольку запись с идентификатором 12 имеет самую последнюю дату.

id      date
1   2012-04-25 00:00:00.000000
2   2012-04-26 00:00:00.000000
3   2012-04-23 00:00:00.000000
4   2012-04-24 00:00:00.000000
5   2012-05-01 00:00:00.000000
6   2012-05-02 00:00:00.000000
7   2012-05-03 00:00:00.000000
8   2012-05-04 00:00:00.000000
9   2012-04-30 00:30:00.000000
10  2012-04-30 18:00:00.000000
11  2012-04-29 00:00:00.000000
12  2012-04-30 18:40:00.000000
13  2012-05-05 00:00:00.000000
14  2012-05-05 09:31:31.000000

Вот (грязная) задача rake для удаления дубликатов

task :remove_duplicate do
  Rake::Task["remove_duplicate"].invoke
end

task :remove_duplicate => :environment do
  weights = Weight.count(:group => "DATE(date)", :having => "COUNT(id) > 1")
  weights_to_delete = []
  weights.each do |weight|

    start_date = weight[0].to_date.beginning_of_day
    end_date = weight[0].to_date.end_of_day
    day_weights = Weight.where("date >= ? and date <= ?", start_date, end_date).order(:date)
    day_weights[0..-2].each do |weight|
      weights_to_delete.push weight.id
    end
  end
  Weight.delete(weights_to_delete)
end

Хотя я могу удалять записи, как я объяснил, я не удовлетворен своим подходом. Пожалуйста, помогите мне удалить повторяющиеся записи на определенную дату, сохранив последнюю, используя только ActiveRecord API.

Спасибо, Амит Патель


person Amit Patel    schedule 02.05.2012    source источник


Ответы (3)


Этот метод может быть медленным, поэтому я не рекомендую его, если вы не используете его периодически.

Weight.all.each do |weight|
  Weight.order("id desc").where(date: weight.date).all.drop(1).each { |w| w.delete }
end
person bricker    schedule 06.05.2012
comment
Медленный и стабильный, но он сделал свою работу, и при использовании в качестве одноразовой операции я предпочитаю читать понятный код, а не скорость. - person lime; 31.07.2013

Попробуй это:

latest_daily_weights = (Weight.maximum :date, :group => 'DATE(date)').values
weights_table = Arel::Table.new(:weights)
earlier_daily_weights = Weight.where(weights_table[:date].not_in latest_daily_weights)
earlier_daily_weights.delete_all

Кредит:

Как исключить массив идентификаторы из запроса в Rails (с использованием ActiveRecord)?

person Steve Rowley    schedule 06.05.2012

Вы можете попробовать этот SQL-запрос, чтобы удалить записи той же даты, но самые последние на эту дату

DELETE FROM weights USING weights weight WHERE (CAST(weights.date as Date) = CAST(weight.date as Date) AND weights.id < weight.id);
person mahendra gawas    schedule 01.06.2015