ID di assegnazione automatica di Rails già esistente


92

Creo un nuovo record in questo modo:

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

Il mio database attualmente ha diverse migliaia di entità per camion, ma ho assegnato gli ID a molti di loro, in un modo che ha lasciato alcuni ID disponibili. Quindi quello che sta succedendo è che rails crea un oggetto con id = 150 e funziona bene. Ma poi prova a creare un elemento e ad assegnarlo id = 151, ma quell'ID potrebbe già esistere, quindi vedo questo errore:

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

E la prossima volta che eseguo l'azione, assegnerà semplicemente l'id 152, che funzionerà bene se quel valore non è già stato preso. Come posso fare in modo che rails verifichi se esiste già un ID prima di assegnarlo?

Grazie!

MODIFICARE

L'ID camion è ciò che viene duplicato. L'utente esiste già ed è una costante in questo caso. In realtà è una questione ereditaria che devo affrontare. Un'opzione è ricreare la tabella in let rails assegna automaticamente ogni id questa volta. Comincio a pensare che questa possa essere la scelta migliore perché ho alcuni altri problemi, ma la migrazione per farlo sarebbe molto complicata perché Truck è una chiave esterna in tante altre tabelle. Ci sarebbe un modo semplice per fare in modo che rails crei una nuova tabella con gli stessi dati già memorizzati in Truck, con ID assegnati automaticamente e mantenendo tutte le relazioni esistenti?


perché non lasciate che i binari assegnino l'ID automaticamente? Ciò eliminerebbe qualsiasi pericolo di duplicazione o si tratta di un problema di dati legacy in cui è necessario conservare i vecchi ID? Voglio solo capire un po 'il business case poiché non è normale quando si crea un nuovo oggetto.
MBHNYC

@MBHNYC Penso che D-Nice stia assegnando un user_id durante la creazione dell'azienda, e non un id come pensi (e l'ho fatto anche per un momento).
Anil

Ooo brava cattura Anil - hai perfettamente ragione. @ D-Nice, forse aggiungi la tua migrazione per questa tabella al tuo post nel caso ci sia qualcosa di strano? Si è tentati di modificare lo user_id per eliminare la confusione ..
MBHNYC

No, mi dispiace non è chiaro, l'ID azienda è ciò che viene duplicato. L'utente esiste già ed è una costante in questo caso. In realtà è una questione ereditaria che devo affrontare. Modificherà il post con maggiori informazioni
D-Nice

Non è necessario ricreare la tabella. Basta vedere la mia risposta per ripristinare la sequenza.
Dondi Michael Stroma

Risposte:


87

Rails sta probabilmente usando la sequenza PostgreSQL incorporata. L'idea di una sequenza è che viene utilizzata una sola volta.

La soluzione più semplice è impostare la sequenza per la colonna company.id sul valore più alto nella tabella con una query come questa:

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

Immagino che il nome della sequenza "company_id_seq", il nome della tabella "company" e il nome della colonna "id" ... si prega di sostituirli con quelli corretti. È possibile ottenere il nome della sequenza con SELECT pg_get_serial_sequence('tablename', 'columname');o guardare la definizione della tabella con \d tablename.

Una soluzione alternativa consiste nel sovrascrivere il metodo save () nella classe aziendale per impostare manualmente l'ID azienda per le nuove righe prima del salvataggio.


Immagino che cosa farebbe è iniziare l'assegnazione automatica con quello che è attualmente il valore più alto + 1?
D-Nice

Penso che questa sia la migliore risposta alla mia domanda, tuttavia, per ragioni non correlate, dovrò trovare un modo per utilizzare la strategia che ho descritto nella mia modifica OP
D-Nice

Non capisco perché sia ​​successo all'inizio? Questo è successo a me e vorrei capire come sia anche possibile.
Hunt Burdick

2
@Websitescenes, se si ha una colonna SERIAL in PostgreSQL (una colonna seriale è quella in cui il valore predefinito è il valore successivo in una sequenza), quindi popola la tabella con valori rigidi in quella colonna, la sequenza non verrà aggiornata automaticamente. Esempio: 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

204

Ho fatto questo che ha risolto il problema per me.

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

Ho trovato reset_pk_sequence! da questo thread. http://www.ruby-forum.com/topic/64428


4
Grazie, la migliore soluzione. Dopo il trasferimento del database ho avuto lo stesso problema.
Oleg Pasko

61
O l'equivalente di una riga (per scopi di copia / incolla della console di rails):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf

Qualche idea su come questo venga fuori sincrono?
Tall Paul

25

Basato sulla risposta di @Apie .

Puoi creare un'attività ed eseguirla quando necessario con:

rake database:correction_seq_id

Crei attività come questa:

rails g task database correction_seq_id

E nel file generato ( lib/tasks/database.rake) inserisci:

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

4

Mi sembra un problema di database e non un problema di Rails. È possibile che il tuo database abbia un seme di identità non corretto sulla tua idcolonna? Per provare, prova a fare un paio di inserimenti direttamente nel tuo database e vedi se esiste lo stesso comportamento.


3
Perché il voto negativo? Questo è il comportamento esatto che si verifica se si imposta la sequenza di incremento su qualcosa che è inferiore ad altri valori esistenti e pertanto occasionalmente si verificano collisioni durante l'inserimento dei dati. Il poster ha già detto che ci sono dati esistenti che rientrano in questo caso.
mynameiscoffey

Posso inserire bene. Dopo aver ricevuto questo errore, posso effettivamente eseguire nuovamente la stessa azione e farlo funzionare, se l'ID successivo nella sequenza non è ancora stato preso.
D-Nice

Sembra che questa fosse la mia situazione - mi sono imbattuto nel problema ma il record successivo che ho inserito ha funzionato bene, quindi deve aver ottenuto il seme nel posto giusto.
Ben Wheeler

3

Ho risolto questo problema seguendo il comando.

Eseguilo nella tua console di rails

ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.