2009-11-10 11 views
6

Tuve que migrar desde una aplicación basada en mySql ruby ​​on rails a usar postgresql. No hay problemas, pero uno hasta ahora, y no sé cómo resolverlo.postgresql nextval generar valores existentes

La migración de datos trajo identificadores junto con ella, y postgresql ahora está teniendo problemas con los identificadores existentes: no está claro para mí de dónde se obtiene el valor que utiliza para determinar la base para nextval: ciertamente no es el valor más alto en la columna, aunque podría pensar que sería una buena idea. En cualquier caso, ahora colisiona con los valores de identificación existentes. Columna de identificación, creado a partir de una migración estándar RoR se define como

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

¿Hay algún lugar que el valor se utiliza como base puede ser hackeado? Este problema ahora podría producirse en cualquiera de 20 o más tablas: Podría utilizar

'select max(id) from <table_name>' 

pero que parece tener la idea de una columna de incremento automático sin sentido.

¿Cómo se maneja mejor?

Respuesta

11

Hay un método reset_pk_sequences! en el Postgres adapter. Puede llamarlo y lo establecerá en max (id) + 1, que es probablemente lo que quiere.

En algunos proyectos obtengo datos ETL con suficiente frecuencia como para justificar una tarea de rastreo para todos los modelos o para un modelo específico. Aquí está la tarea - incluirlo en alguna Rakefile o en su propio bajo lib/tareas:

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 

Ahora se puede ejecutar este sea para todos los modelos (definidos en RAIS_ROOT/app/models) utilizando rake reset_sequences, o para una específica modelo al pasar un nombre de clase.

+0

Wow, gracias por todas las prontas respuestas y útiles. Me echaré a correr en la solución @hgimenez, porque estoy en un entorno de Rails, pero supongo que el mensaje es que puedo hacerlo a través de la línea de comandos en postgres. Como seguimiento: voy a intentar esto, pero ¿puedo poner una declaración así en una migración? –

+0

Definitivamente, puede tener una migración que vaya: ActiveRecord :: Base.connection.reset_pk_sequence! ('Table_name'), pero esto también se puede hacer desde psql. – hgmnz

+0

+1 punto por genialidad – kikito

0

Uso setval() para establecer el valor de partida para la secuencia.

3

con esa definición, la columna obtendrá el siguiente valor de la secuencia geopoints_id_seq. Esa secuencia no está directamente asociada a la tabla. Si está migrando datos, debe crear o actualizar esa secuencia para que su punto de partida sea mayor que el ID máximo actual en su tabla.

Debería poder establecer su nuevo valor con p. Ej.

ALTER SEQUENCE geopoints_id_seq RESTART with 1692; 

O lo que sea, seleccione max (id) from table_name; cede

5

Los carriles 3 versión se parece a esto:

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

Creo que 'RAILS_ROOT' debe ser' Rails.root.to_s' en su lugar. Además, esa sintaxis de 'tarea 'está en desuso. Creo que debería ser 'task: reset_sequences, [: model_class] => [: environment]' –

Cuestiones relacionadas