Railsy automatycznie przypisują identyfikator, który już istnieje

93

Tworzę nowy rekord w ten sposób:

truck = Truck.create(:name=>name, :user_id=>2)

Moja baza danych zawiera obecnie kilka tysięcy podmiotów dla ciężarówki, ale przypisałem identyfikatory do kilku z nich w sposób, który pozostawił dostępne identyfikatory. Więc co się dzieje, to rails tworzy element o id = 150 i działa dobrze. Ale potem próbuje utworzyć element i przypisać mu id = 151, ale ten identyfikator może już istnieć, więc widzę ten błąd:

ActiveRecord::RecordNotUnique (PG::Error: ERROR: duplicate key value violates unique constraint "companies_pkey" DETAIL: Key (id)=(151) already exists.

Następnym razem, gdy uruchomię tę akcję, po prostu przypisze identyfikator 152, który będzie działał dobrze, jeśli ta wartość nie jest już pobrana. Jak mogę sprawić, by railsy sprawdzały, czy identyfikator już istnieje, zanim go przypisuje?

Dzięki!

EDYTOWAĆ

Duplikuje się identyfikator ciężarówki. Użytkownik już istnieje i jest w tym przypadku stałą. W rzeczywistości jest to problem, z którym muszę się uporać. Jedną z opcji jest ponowne utworzenie tabeli przy pomocy niech rails auto przypisuje tym razem każdy identyfikator. Zaczynam myśleć, że to może być najlepszy wybór, ponieważ mam kilka innych problemów, ale migracja w tym celu byłaby bardzo skomplikowana, ponieważ ciężarówka jest kluczem obcym w wielu innych tabelach. Czy istniałby prosty sposób, aby szyny tworzyły nową tabelę z tymi samymi danymi, które są już zapisane w ciężarówce, z automatycznie przypisywanymi identyfikatorami i zachowując wszystkie istniejące relacje?

D-Nice
źródło
dlaczego nie pozwalasz szynom automatycznie przypisywać ID? To wyeliminowałoby jakiekolwiek niebezpieczeństwo powielenia - czy jest to problem ze starszymi danymi, w którym musisz zachować stare identyfikatory? Po prostu chcę trochę zrozumieć uzasadnienie biznesowe, ponieważ nie jest to normalne podczas tworzenia nowego obiektu.
MBHNYC
@MBHNYC Myślę, że D-Nice przypisuje user_id podczas tworzenia firmy, a nie id, jak myślisz (i przez chwilę to zrobiłem).
Anil
Ooo, dobrze złap Anila - masz całkowitą rację. @ D-Nice, być może dodaj swoją migrację dla tej tabeli do swojego postu, na wypadek gdyby było coś dziwnego? Kuszące, aby edytować identyfikator user_id, aby wyeliminować zamieszanie.
MBHNYC,
Nie, przepraszam, to było niejasne, identyfikator firmy jest powielany. Użytkownik już istnieje i jest w tym przypadku stałą. W rzeczywistości jest to problem, z którym muszę się uporać. Będzie edytować post z większą ilością informacji
D-Nice
Nie musisz odtwarzać tabeli. Zobacz moją odpowiedź, aby zresetować sekwencję.
Dondi Michael Stroma

Odpowiedzi:

88

Railsy prawdopodobnie używają wbudowanej sekwencji PostgreSQL. Idea sekwencji polega na tym, że jest ona używana tylko raz.

Najprostszym rozwiązaniem jest ustawienie kolejności dla kolumny company.id na najwyższą wartość w tabeli za pomocą zapytania takiego:

SELECT setval('company_id_seq', (SELECT max(id) FROM company));

Zgaduję, że nazwa Twojej sekwencji „company_id_seq”, nazwa tabeli „company” i nazwa kolumny „id”… proszę zastąpić je poprawnymi. Możesz pobrać nazwę sekwencji za pomocą SELECT pg_get_serial_sequence('tablename', 'columname');lub spojrzeć na definicję tabeli za pomocą \d tablename.

Alternatywnym rozwiązaniem jest zastąpienie metody save () w klasie firmy, aby ręcznie ustawić identyfikator firmy dla nowych wierszy przed zapisaniem.

Dondi Michael Stroma
źródło
Zgaduję, co by to zrobiło, gdyby automatyczne przypisywanie zaczęło się od tego, jaka jest obecnie najwyższa wartość + 1?
D-Nice
Myślę, że to najlepsza odpowiedź na moje pytanie, jednak z niepowiązanych powodów będę musiał znaleźć sposób na wykorzystanie strategii, którą opisałem w moim wydaniu OP
D-Nice
Nie rozumiem, dlaczego to się zaczęło? Przydarzyło mi się to i chciałbym zrozumieć, jak to w ogóle możliwe.
Hunt Burdick
2
@Websitescenes, jeśli ktoś ma kolumnę SERIAL w PostgreSQL (kolumna szeregowa to taka, w której wartość domyślna jest następną wartością w sekwencji), a następnie zapełni tabelę twardymi wartościami w tej kolumnie, sekwencja nie zostanie automatycznie zaktualizowana. Przykład: create table t (id serial not null primary key); insert into t values (1); insert into t values (default); ERROR: duplicate key value violates unique constraint "t_pkey" DETAIL: Key (id)=(1) already exists.
Dondi Michael Stroma
207

Zrobiłem to, co rozwiązało problem.

ActiveRecord::Base.connection.tables.each do |t|
  ActiveRecord::Base.connection.reset_pk_sequence!(t)
end

Znalazłem reset_pk_sequence! z tego wątku. http://www.ruby-forum.com/topic/64428

Ciasto
źródło
4
Dzięki, najlepsze rozwiązanie. Po przeniesieniu bazy danych miałem ten sam problem.
Oleg Pasko
63
Lub odpowiednik jednowierszowy (do kopiowania / wklejania konsoli):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf
Masz jakiś pomysł, jak to się nie synchronizuje?
Tall Paul
26

Na podstawie odpowiedzi @Apie .

Możesz wykonać zadanie i uruchomić je, kiedy potrzebujesz:

rake database:correction_seq_id

Tworzysz takie zadania:

rails g task database correction_seq_id

A w wygenerowanym pliku ( lib/tasks/database.rake) umieść:

namespace :database do
    desc "Correction of sequences id"
    task correction_seq_id: :environment do
        ActiveRecord::Base.connection.tables.each do |t|
            ActiveRecord::Base.connection.reset_pk_sequence!(t)
        end
    end
end
inye
źródło
4

Brzmi to jak problem z bazą danych, a nie z Railsami. Czy jest możliwe, że Twoja baza danych zawiera nieprawidłowe ziarno tożsamości w idkolumnie? Aby przetestować, spróbuj wykonać kilka operacji wstawiania bezpośrednio do bazy danych i sprawdź, czy istnieje takie samo zachowanie.

mynameiscoffey
źródło
3
Dlaczego głos przeciw? To jest dokładnie to zachowanie, które ma miejsce, jeśli ustawisz sekwencję inkrementacji na wartość niższą niż inne istniejące wartości i dlatego czasami dochodzi do kolizji podczas wstawiania danych. Plakat już powiedział, że istnieją dane, które dotyczą tej sprawy.
mynameiscoffey
Mogę wstawić dobrze. Po pojawieniu się tego błędu mogę ponownie uruchomić tę samą akcję i sprawić, by zadziałała, jeśli następny identyfikator w sekwencji nie jest jeszcze pobrany.
D-Nice,
wygląda na to, że to była moja sytuacja - napotkałem problem, ale następny rekord, który wstawiłem, działał dobrze, więc musiał umieścić ziarno we właściwym miejscu.
Ben Wheeler
4

Rozwiązałem ten problem, wykonując polecenie.

Uruchom to w konsoli railsów

ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Jigar Bhatt
źródło