2016-01-23 1 views
1

Рубина 2.3.0, Rails 4.2.4, а на самом деле с помощью PostGreSQL вместо SQLiteОбеспечение Rails базы данных запись уникальности при обновлении без прерывания процесса обновления

Обновлены для ясности

У меня есть большой файл CSV файл (обновляемый извне & загружен ежедневно) и написал метод обновления таблицы базы данных Rails. Я не хочу, чтобы метод добавлял все строки в базу данных без проверки уникальности, поэтому я использую это замечательное решение (How do I make a column unique and index it in a Ruby on Rails migration?) с add_index.

Я использую файл рейка для хранения исполняемого кода обновления, и я ввожу $ rake update_task в свой терминал (который работает, если таблица не имеет дубликатов с импортированными строками csv). Проблема заключается в том, что база данных ABORTS (rake aborted!) рейка, когда она встречает первую двойную запись (ERROR: duplicate key value violates unique constraint).

Что я могу сделать, чтобы удалить/не сохранить дубликаты, избегая прерывания/сбоя? Я не могу просто отбросить таблицу базы данных и перезагрузить ее каждый день. Вот схема:

ActiveRecord::Schema.define(version: 20160117172450) do 

# These are extensions that must be enabled in order to support this database 
enable_extension "plpgsql" 

    create_table "tablename", force: :cascade do |t| 
    t.string "attr1" 
    t.string "attr2" 
    t.string "attr3" 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
    end 

    add_index "tablename", ["attr1", "attr2", "attr3"], name: "index_tablename_on_attr1_and_attr2_and_attr3", unique: true, using: :btree 

end 

и моя задача рек в Lib/задачах/содержании file_name.rake:

desc "Download data and update database table" 

task :update_task => :environment do 
    u = CorrectClassName.new 
    u.perform_this 
end 

и CorrectClassName находится в .rb файл в приложении/каталоге directory1:

class CorrectClassName 

    def perform_this 
    something = ClassWithUpdateCode.new 
    something.update_database 
    end 

end 

и ClassWithUpdateCode находится в .rb файл в приложение/каталог directory2:

require 'csv' 

class ClassWithUpdateCode 

    def update_database 
    csv_update = File.read(Rails.root.join('lib', 'assets', "file_name.csv")) 
    options = {:headers => true} 

    csv = CSV.parse(csv_update, options) 
    csv.each do |row| 
     tm = TableModel.new 

     tm.attr1 = row[0] 
     tm.attr2 = row[1] 
     tm.attr3 = row[2] 
     tm.save # maybe I can use a different method or if statement here? 
    end 
    end 

end 

Update: @ решение Kristan работает ниже, но вот где поставить обработку начать/спасения/окончания:

В .rb файл в приложение/каталог directory2:

require 'csv' 

class ClassWithUpdateCode 

    def update_database 
    csv_update = File.read(Rails.root.join('lib', 'assets', "file_name.csv")) 
    options = {:headers => true} 

    csv = CSV.parse(csv_update, options) 
    csv.each do |row| 
     tm = TableModel.new 
     begin 
      tm.attr1 = row[0] 
      tm.attr2 = row[1] 
      tm.attr3 = row[2] 
      tm.save 
     rescue ActiveRecord::RecordNotUnique 
     end 
    end 
    end 

end 
+0

Какую версию PostgreSQL вы используете? 9.5 добавляет поддержку. 'INSERT ... ON CONFLICT DO NOTHING' –

+0

Это может очень хорошо работать (я использую 9.5), Том ... Я просто не пробовал сырой SQL раньше! В какой-то момент в будущем мне нужно будет ускориться, не используя синтаксический анализ csv и, возможно, иметь/копировать и, возможно, ваше предложение SQL здесь. – JHFirestarter

ответ

1

rake является когда возникает исключение, когда вы пытаетесь сохранить запись, которая нарушает ограничение уникальности вашей таблицы. Самый простой способ предотвратить это - это поймать и игнорировать исключение. Я предполагаю, что ваша запись создана во время u.perform_this.

task :update_task => :environment do 
    u = CorrectClassName.new 
    begin 
    u.perform_this 
    rescue ActiveRecord::RecordNotUnique 
    # move on 
    end 
end 

Другой вариант заключается в добавлении uniqueness validation к модели Rails, то либо проверить valid? перед сохранением или вызвать create (не create!), который не вызывает исключения проверки.

class CorrectClassName < ActiveRecord::Base 
    validates_uniqueness_of :attr1, scope: [:attr2, :attr3] 
end 
task :update_task => :environment do 
    u = CorrectClassName.new(data) 
    u.perform_this if u.valid? 
end 
+0

Я был, вероятно, слишком многословным и также должен был включить базовый метод в «perform_this». Решение должно (а) соблюдать валидацию уникальности без (б) привлечения исключений и прерывания/неудачи.Когда я попробовал решение start/rescue/end, (b) было решено, но не (a) - в таблице теперь есть дубликаты. То же самое с "if u.valid?" (даже если «если u.valid?» используется в сочетании с методом perform_this .save ... который я только что обновил в случае, если есть решение). – JHFirestarter

+0

Если у вас есть уникальный индекс базы данных по этим атрибутам, невозможно получить дубликаты. Вы уверены, что правильно настроили? –

+0

А, интересно ... Мне нужно было удалить attr3 как ограничение уникальности - спасибо, что помогли мне найти это! Тем не менее, без оскорбительного ограничения attr3, обработка begin/rescue/end устраняет исключения, которые возникают ... но таблица не обновляется (поэтому задача rake прерывается каким-то образом). Есть ли эквивалентный синтаксис «переход к следующему», который я должен использовать между «rescue» и «end»? – JHFirestarter

Смежные вопросы