2012-06-17 15 views
75

utworzyć nowy rekord tak:Szyny automatycznego przypisywania identyfikator, który już istnieje

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

Moja baza ma obecnie kilka tysięcy jednostek do wózka, ale przypisany identyfikator do kilku z nich, w sposób sposób, dzięki któremu niektóre id są dostępne. Tak więc, co się dzieje, szyny tworzą element o id = 150 i działa dobrze. Ale potem próbuje utworzyć element i przypisać mu id = 151, ale id 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.

i następnym razem mogę uruchomić akcję, to będzie po prostu przypisz identyfikator 152, który będzie działał poprawnie, jeśli ta wartość nie jest już zajęta. Jak mogę uzyskać tory, aby sprawdzić, czy identyfikator już istnieje, zanim go przypisze?

Dzięki!

EDYTOWANIE

Identyfikator wózka jest tym, co jest powielane. Użytkownik już istnieje i jest w tym przypadku stały. To jest stary problem, z którym muszę sobie poradzić. Jedną z opcji jest ponowne utworzenie tabeli przy automatycznym przypisywaniu każdego identyfikatora. Zaczynam myśleć, że to może być najlepszy wybór, ponieważ mam kilka innych problemów, ale migracja do tego będzie bardzo skomplikowana, ponieważ Truck jest obcym kluczem w wielu innych tabelach. Czy istnieje prosty sposób, aby szyny tworzyły nową tabelę z tymi samymi danymi, które są już przechowywane w Truck, z automatycznie przypisanymi identyfikatorami i utrzymywaniem wszystkich istniejących relacji?

+0

dlaczego nie pozwalasz automatom przypisywać identyfikatora automatycznie? Wyeliminowałoby to niebezpieczeństwo duplikacji - czy jest to problem ze starszymi danymi, w którym należy zachować stare identyfikatory? Po prostu chcesz zrozumieć sprawę biznesową, ponieważ nie jest to normalne w przypadku tworzenia nowego obiektu. – MBHNYC

+0

@MBHNYC Myślę, że D-Nice przypisuje user_id podczas tworzenia firmy, a nie id, jak myślisz (i zrobiłem to na chwilę). – Anil

+0

Ooo dobry połów Anil - masz całkowitą rację. @ D-Nice, może dodać swoją migrację do tego stołu do swojego posta, czy jest coś dziwnego? Kuszące, aby edytować ten identyfikator użytkownika, aby wyeliminować zamieszanie .. – MBHNYC

Odpowiedz

75

Szyny prawdopodobnie używają wbudowanej sekwencji PostgreSQL. Ideą sekwencji jest to, że jest używana tylko raz.

Najprostszym rozwiązaniem jest ustawienie sekwencji do company.id kolumny do najwyższej wartości w tabeli z kwerendy jak poniżej:

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

Domyślam się nazwy sekwencji „company_id_seq” tabeli nazwa "firma" i nazwa kolumny "id" ... proszę zastąpić je poprawnymi. Możesz uzyskać nazwę sekwencji za pomocą SELECT pg_get_serial_sequence('tablename', 'columname'); lub przejrzeć definicję tabeli za pomocą \d tablename.

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

+0

Zgaduję, co by to miało mieć automatyczne przydzielanie początku z wartością, która obecnie jest najwyższa + 1? –

+0

To prawda, przepraszam, że nie wyjaśniłem tego dokładniej. –

+0

Myślę, że jest to najlepsza odpowiedź na moje pytanie, jednak z niezwiązanych z nią przyczyn, będę musiał znaleźć sposób, aby użyć strategii opisanej w mojej edycji OP –

3

Brzmi dla mnie jak problem z bazą danych, a nie problem z szynami. Czy jest możliwe, że baza danych zawiera niewłaściwe nasienie tożsamości w kolumnie id? Aby przetestować, spróbuj wykonać kilka wstawek bezpośrednio do bazy danych i sprawdź, czy istnieje takie samo zachowanie.

+3

Dlaczego upadek? Jest to dokładnie zachowanie zachodzące w przypadku ustawienia sekwencji przyrostowej na coś, co jest niższe niż inne istniejące wartości, a zatem od czasu do czasu uderza kolizje podczas wstawiania danych. Plakat już powiedział, że istnieją dane, które należą do tej sprawy. – mynameiscoffey

+0

Mogę wstawić dobrze. Po otrzymaniu tego błędu mogę ponownie uruchomić tę samą akcję i uruchomić ją, jeśli następny identyfikator w sekwencji nie został jeszcze podjęty. –

+0

wydaje się, że to była moja sytuacja - wpadłem na ten problem, ale następny zapis, który wstawiłem, działał dobrze, więc musiał dostać ziarno w odpowiednie miejsce. –

181

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

+4

Dzięki, najlepsze rozwiązanie. Po przeniesieniu bazy danych miałem ten sam problem. –

+54

Albo ekwiwalent jednej linijki (do kopiowania/wklejania konsoli szyny): 'ActiveRecord :: Base.connection.tables.each {| t | ActiveRecord :: Base.connection.reset_pk_sequence! (T)} ' – Raf

+4

należy przyjąć odpowiedź !! oneone –

24

Na podstawie @Apie answer.

Można wykonać zadanie i uruchomić, kiedy trzeba z:

rake database:correction_seq_id 

utworzyć zadania tak:

rails g task database correction_seq_id 

iw pliku wygenerowanego (lib/tasks/database.rake) umieścić:

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 
+3

Najlepsze rozwiązanie, dziękuję. – monteirobrena

Powiązane problemy