2009-11-10 2 views
6

Мне пришлось перенести из рубинового сайта на основе mySql на приложение rails, используя postgresql. Нет проблем, кроме одного, и я не знаю, как его решить.postgresql nextval, генерирующий существующие значения

Миграция данных привела идентификаторы вместе с ним, а postgresql теперь имеет проблемы с существующими идентификаторами: мне не ясно, где она получает значение, которое оно использует для определения базы для nextval: это, конечно, не самое высокое значение в столбце, хотя вы можете подумать, что это будет хорошей идеей. В любом случае, теперь он сталкивается с существующими значениями id. Идентификатор столбца, созданный из стандартной миграции RoR определяется как

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

Есть ли какое-то место, что значение, которое используется в качестве основы может быть взломан? Эта проблема может возникнуть в настоящее время ни в одной из 20 или около того столов: я мог бы использовать

'select max(id) from <table_name>' 

, но это, кажется, делает идею колонки автоинкремента бессмысленной.

Как это лучше всего обрабатывается?

ответ

11

Метод reset_pk_sequences! на Postgres adapter. Вы можете вызвать его, и он установит его на max (id) + 1, что, вероятно, вам нужно.

В некоторых проектах я получаю данные ETL'ed достаточно часто, чтобы оправдать задачу рейка, чтобы сделать это для всех моделей или для определенной модели. Вот задача - включить его в какой-то Rakefile или в своем собственном под Lib/задачи:

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 

Теперь вы можете запустить либо для всех моделей (определяемых при RAIS_ROOT/приложения/модели) с использованием rake reset_sequences, или для конкретного модели, передав имя класса.

+0

Ого, спасибо за быстрые и полезные ответы на все. Я забегу на решение @hgimenez, потому что я в среде Rails, но я полагаю, что сообщение состоит в том, что я могу сделать это через командную строку в postgres. В качестве продолжения: я собираюсь попробовать это, но могу ли я привести такое заявление в миграцию? –

+0

Определенно, вы можете выполнить миграцию: ActiveRecord :: Base.connection.reset_pk_sequence! ('Table_name'), но это, безусловно, можно сделать и из psql. – hgmnz

+0

+1 точка для awesomeness – kikito

1

PG использует последовательности:

Сделать это текущее значение 1 выше, чем самое высокое значение в вашей таблице, как это.

SELECT setval('geopoints_id_seq', 999999999, true); 

см Также эти

http://www.postgresql.org/docs/8.4/interactive/datatype-numeric.html#DATATYPE-SERIAL

http://www.postgresql.org/docs/8.4/interactive/functions-sequence.html

0

Использование setval(), чтобы установить начальное значение для последовательности.

3

с этим определением столбец получит следующее значение из последовательности geointoints_id_seq. Эта последовательность не привязана непосредственно к таблице. Если вы переносите данные, вам необходимо создать или обновить эту последовательность, чтобы ее начальная точка была больше, чем текущий максимальный id в вашей таблице.

Вы должны иметь возможность установить новое значение, например.

ALTER SEQUENCE geopoints_id_seq RESTART with 1692; 

Или что угодно, выберите max (id) из table_name;

5

Рельсы 3 версии выглядит следующим образом:

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

Я думаю, что 'RAILS_ROOT' должно быть' Rails.root.to_s' вместо этого. Кроме того, этот синтаксис 'task' устарел. Я думаю, что это должна быть задача: reset_sequences, [: model_class] => [: environment] ' –

Смежные вопросы