2009-11-10 4 views
6

Ho dovuto migrare da un ruby ​​basato su mySql sull'app rails all'utilizzo di postgresql. Nessun problema tranne uno finora, e non so come risolverlo.postgresql nextval che genera valori esistenti

La migrazione di dati ha portato id insieme a esso e Postgresql ha ora problemi con gli ID esistenti: non mi è chiaro dove ottiene il valore che usa per determinare la base per nextval: non è certamente il il valore più alto nella colonna, anche se potresti pensare che sarebbe una buona idea. In ogni caso, sta entrando in collisione con i valori di identificazione esistenti. colonna id, creato da una migrazione standard RoR è definita come

not null default nextval('geopoints_id_seq'::regclass) 

C'è qualche posto che il valore utilizza come base può essere violato? Questo problema potrebbe ora verificarsi in uno qualsiasi dei 20 o giù di tabelle: ho potuto utilizzare

'select max(id) from <table_name>' 

ma che sembra rendere l'idea di una colonna autoincrement inutile.

Come viene gestito meglio questo?

risposta

11

C'è un metodo reset_pk_sequences! su Postgres adapter. Puoi chiamarlo e lo imposterà su max (id) + 1, che è probabilmente quello che vuoi.

In alcuni progetti ottengo dati ETL in abbastanza spesso da giustificare un rake task per fare questo per tutti i modelli, o per un modello specificato. Ecco il compito - includerlo in qualche Rakefile o in esso la propria sotto lib/task:

desc "Reset all sequences. Run after data imports" 
task :reset_sequences, :model_class, :needs => :environment do |t, args| 
    if args[:model_class] 
    classes = Array(eval args[:model_class]) 
    else 
    puts "using all defined active_record models" 
    classes = [] 
    Dir.glob(RAILS_ROOT + '/app/models/**/*.rb').each { |file| require file } 
    Object.subclasses_of(ActiveRecord::Base).select { |c| 
     c.base_class == c}.sort_by(&:name).each do |klass| 
     classes << klass 
     end 
    end 
    classes.each do |klass| 
     next if klass == CGI::Session::ActiveRecordStore::Session && ActionController::Base.session_store.to_s !~ /ActiveRecordStore/ 

     puts "reseting sequence on #{klass.table_name}" 
     ActiveRecord::Base.connection.reset_pk_sequence!(klass.table_name) 
    end 
end 

Ora è possibile eseguire questa operazione per tutti i modelli (definiti sotto RAIS_ROOT/app/modelli) utilizzando rake reset_sequences, o per una specifica modella passando un nome di classe.

+0

Wow, grazie per tutte le risposte tempestive e utili. Farò una corsa alla soluzione @hgimenez, perché sono in un ambiente Rails, ma presumo che il messaggio è che posso farlo tramite la riga di comando in postgres. Come follow-up: ho intenzione di provare questo, ma posso mettere una tale affermazione in una migrazione? –

+0

Sicuramente, si può avere una migrazione che va: ActiveRecord :: Base.connection.reset_pk_sequence! ('Table_name'), ma questo può certamente essere fatto anche da psql. – hgmnz

+0

+1 point per awesomeness – kikito

0

Usa setval() per impostare il valore iniziale della sequenza.

3

con tale definizione, la colonna otterrà il valore successivo dalla sequenza geopoints_id_seq. Questa sequenza non è direttamente collegata alla tabella. Se si sta eseguendo la migrazione dei dati, è necessario creare o aggiornare tale sequenza in modo che il suo punto di partenza sia maggiore dell'ID massimo corrente nella tabella.

Dovresti essere in grado di impostare il suo nuovo valore con ad es.

ALTER SEQUENCE geopoints_id_seq RESTART with 1692; 

O qualsiasi altra selezione max (id) da nome_tabella; yield

5

I binari 3 versione si presenta così:

namespace :db do 
    desc "Reset all sequences. Run after data imports" 
    task :reset_sequences, :model_class, :needs => :environment do |t, args| 
    if args[:model_class] 
     classes = Array(eval args[:model_class]) 
    else 
     puts "using all defined active_record models" 
     classes = [] 
     Dir.glob(RAILS_ROOT + '/app/models/**/*.rb').each { |file| require file } 
     ActiveRecord::Base.subclasses.select { |c|c.base_class == c}.sort_by(&:name).each do |klass| 
     classes << klass 
     end 
    end 
    classes.each do |klass| 
     puts "reseting sequence on #{klass.table_name}" 
     ActiveRecord::Base.connection.reset_pk_sequence!(klass.table_name) 
    end 
    end 
end 

https://gist.github.com/909032

+2

Penso che "RAILS_ROOT" debba essere "Rails.root.to_s". Inoltre, la sintassi 'task' è deprecata. Penso che dovrebbe essere 'task: reset_sequences, [: model_class] => [: environment]' –