Wrzucanie unikatowych wartości do bazy

Witam, mam takie pytanie jak wrzucić do tabeli wiele rekordów za jednym raze sprawdzając czy dany rekord istnieje w bazie, jeśli tak to tworzenie ma być pominięte?

Czy istnieje w miare jakiś szybki i nie obciążający aplikacji sposób? CZy za każdym razem jak tworzony jest dany rekord mam sprawdzić czy już istnieje taki sam w tabeli? Z tym że będzie strasznie duża złożoność takiego algorytmu. Znacie jakieś lepsze rozwiązanie oczywiście w Railsach?

Utworz migracje add_index :tabela, :id, :unique => true baza dany zajmie sie tym problemem. W aplikacji bedziesz zmuszony dodac pewnie przy save albo create jakies rescue nil albo jakos inaczej obsluzyc wyjatek.

Najprostszy sposób to użycie walidacji validates_uniqueness_of. Wedy Railsy przed zapisem sprawdzą, czy nie istnieje już rekord z taką samą wartością w podanym polu. Będzie dodatkowe zapytanie do bazy (pewnie select count).

Jeśli masz bardzo dużą tabelę i nie możesz sobie pozwolić na dodatkowe zapytanie, to zrób tak jak napisał warszk, łapiąc wyjątek przy zapisie.

Ale czy walidacja przy wychwyceniu wyjątku pozwoli na przejście do dalszych rekordów w celu ich zapisania, czy jak się domyślam wychwyci wyjątek i nie pozwoli na dalszy zapis? Mam w tabeli dwie kolumny które chciałbym mieć unikatowe. Czy dodanie atrybutu unique dla obu kolumn jest poprawne?

Po wyłapaniu wyjątku możesz robić co chcesz, atrybut unique możesz dodawać na dowolne kolumny.

Ale właściwie po co tak kombinujesz? Nie lepiej wstawić walidację, o której pisałem?

Łapiesz wyjątek, robisz z nim co chcesz (np. logujesz gdzieś to info, albo ignorujesz zupełnie) i przechodzisz do zapisu kolejnego rekordu. I cała akcja powtarza się od początku. Atrybut unique możesz dać na pojedyncze kolumny albo na kilka na raz. Np. jeden autor może mieć tylko jeden wpis z takim samym numerem ISBN.

add_column :nazwa_tabeli, [:author, :isbn], :name => "author_isbn_index", :unique => true

Dodaj unikalny index do bazy.

Zacznij transakcje
Najpierw zrób selecta który znajdzie elementy, co już są w bazie. Wyrzuć je ze zbioru tych, które chcesz insertować.
Ze zbioru zbuduj z ręki string będący dużym insertem wsadzającym wiele wierszy do bazy.
ActiveRecord::Base.connection.execute(zbudowany_sqlowy_string)
Skończ transakcję.

Wariant zakłada, że wstawiasz tak dużo rekordów do bazy, że jest to jedyne sensowne rozwiązanie. W przeciwnym wypadku jest dużo prostszych sposobów. Ale jak masz insertować po kilka tysięcy elementów to bez używania czystego sql sie nie obejdzie raczej, moim zdaniem.

Witam, dzięki za inforamcje zrobiłem tak: założyłem index na bazie przechwyciłem błąd za pomocą rescue i wszystko chodzi tak jak należy, nie musiałem ustawiać walidacji. Rekord składa się z kolumn o nazwie :tite, :link oraz :bookmark_id. Ten rekord ma być unikatowy to znaczy nie może się powtarzać taki sam w bazie. Kontroli współbieżności tutaj nie potrzebuję chyba że jedynie przy tworzeniu przez dwóch różnych użytkowników w tej samej chwili obiektu Bookmark.