2012-06-17 16 views
75

ho creare un nuovo record in questo modo:Rails id auto-assegnazione che esiste già

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

mio database ha attualmente diverse migliaia di soggetti per il camion, ma ho assegnato l'ID di per molti di loro, in un modo che ha lasciato alcuni ID disponibili. Quindi, quello che sta succedendo è che rails crea oggetti con id = 150 e funziona perfettamente. Ma poi si cerca di creare un elemento e assegnarlo id = 151, ma che id potrebbe già esistere, quindi sto vedendo 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 eseguire l'azione, lo farà semplicemente assegna l'id 152, che funzionerà correttamente se quel valore non è già stato preso. Come posso ottenere le rotaie per verificare se un ID esiste già prima di assegnarlo?

Grazie!

EDIT

L'id Truck è ciò che viene duplicato. L'utente esiste già ed è una costante in questo caso. In realtà è un problema ereditario che devo affrontare. Un'opzione, è ricreare il tavolo a lasciare che le rotaie assegnino automaticamente ogni id questa volta. Sto iniziando a pensare che questa potrebbe essere la scelta migliore perché ho qualche altro problema, ma la migrazione per farlo sarebbe molto complicata perché Truck è una chiave straniera in tanti altri tavoli. Ci sarebbe un modo semplice per far sì che le rotaie creino una nuova tabella con gli stessi dati già archiviati in Truck, con ID assegnati automaticamente e mantenendo tutte le relazioni esistenti?

+0

perché non stai permettendo alle rotaie di assegnare automaticamente l'ID? 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 dato che questo non è business-as-usual quando si crea un nuovo oggetto. – MBHNYC

+0

@MBHNYC Penso che D-Nice stia assegnando un user_id durante la creazione della società, e non id come stai pensando (e l'ho fatto anche per un momento). – Anil

+0

Ooo buona cattura Anil - hai perfettamente ragione. @ D-Nice, forse aggiungi la tua migrazione per questo tavolo al tuo post in caso ci sia qualcosa di strano? Tentativo di eliminare quell'utente_id per eliminare la confusione. – MBHNYC

risposta

75

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

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

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

sto indovinando il tuo nome sequenza "company_id_seq", tavolo nome "azienda" e nome colonna "id" ... per favore sostituiscili 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 è sostituire il metodo save() nella classe della società per impostare manualmente l'ID società per le nuove righe prima di salvare.

+0

Immagino che cosa potrebbe fare l'inizio dell'assegnazione automatica con quello che attualmente è il valore più alto + 1? –

+0

Questo è corretto, mi dispiace non l'ho spiegato più a fondo. –

+0

Penso che questa sia la migliore risposta alla mia domanda, tuttavia, per ragioni non correlate, ho intenzione di trovare un modo per usare la strategia che ho descritto nel mio OP edit –

3

Mi sembra un problema di database e non un problema di Rails. È possibile che il tuo database abbia una seed di identità impropria sulla tua colonna id? Per provare prova a fare un paio di inserimenti direttamente nel tuo database e vedere se esiste lo stesso comportamento.

+3

Perché il downvote? Questo è il comportamento esatto che accade se si imposta la sequenza di incremento su qualcosa che è inferiore rispetto ad altri valori esistenti e quindi occasionalmente colpisce collisioni quando si inseriscono dati. Il poster ha già detto che ci sono dati esistenti che rientrano in questo caso. – mynameiscoffey

+0

Posso inserire bene. Dopo aver ricevuto questo errore, posso effettivamente eseguire nuovamente la stessa azione e farlo funzionare, se il prossimo id nella sequenza non è ancora stato preso. –

+0

sembra che questa fosse la mia situazione - mi sono imbattuto nel problema ma il prossimo record che ho inserito ha funzionato bene, quindi è necessario che il seme sia arrivato nel posto giusto. –

181

Ho fatto questo per risolvere il problema per me.

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

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

+4

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

+54

Oppure equivalente a un solo liner (per scopi di copia/incolla della console di rails): 'ActiveRecord :: Base.connection.tables.each {| t | ActiveRecord :: Base.connection.reset_pk_sequence! (T)} ' – Raf

+4

dovrebbe essere accettato risposta !! oneone –

24

Basato su @Apie answer.

È possibile effettuare un'operazione ed eseguire quando si ha bisogno di:

rake database:correction_seq_id 

si creano attività in questo modo:

rails g task database correction_seq_id 

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

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

La migliore soluzione, grazie. – monteirobrena

Problemi correlati