Rails автоматически назначает идентификатор, который уже существует

93

Создаю новую запись вот так:

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

В моей базе данных сейчас есть несколько тысяч объектов для грузовиков, но я назначил идентификаторы нескольким из них, так что некоторые идентификаторы остались доступными. Итак, что происходит, rails создает элемент с id = 150, и он отлично работает. Но затем он пытается создать элемент и присвоить ему id = 151, но этот идентификатор может уже существовать, поэтому я вижу эту ошибку:

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

И в следующий раз, когда я запущу действие, ему просто будет назначен идентификатор 152, который будет работать нормально, если это значение еще не принято. Как я могу заставить рельсы проверять, существует ли уже идентификатор, прежде чем он его назначит?

Благодарность!

РЕДАКТИРОВАТЬ

Идентификатор грузовика - это то, что дублируется. Пользователь уже существует и в данном случае является постоянным. На самом деле это проблема наследия, с которой мне приходится иметь дело. Один из вариантов - воссоздать таблицу, чтобы на этот раз рельсы автоматически назначали каждый идентификатор. Я начинаю думать, что это может быть лучший выбор, потому что у меня есть несколько других проблем, но миграция для этого была бы очень сложной, потому что Truck является внешним ключом во многих других таблицах. Будет ли простой способ заставить рельсы создать новую таблицу с теми же данными, которые уже хранятся в Truck, с автоматически назначенными идентификаторами и сохранением всех существующих отношений?

D-Nice
источник
почему вы не позволяете рельсам автоматически присваивать ID? Это устранило бы любую опасность дублирования - или это проблема с устаревшими данными, когда вы должны сохранять старые идентификаторы? Просто хочу немного понять бизнес-кейс, поскольку при создании нового объекта это не обычная практика.
MBHNYC
@MBHNYC Я думаю, что D-Nice назначает user_id при создании компании, а не id, как вы думаете (и я тоже на мгновение).
Anil
Ооо хороший улов Анил - ты совершенно прав. @ D-Nice, возможно, добавьте миграцию для этой таблицы в свой пост, если есть что-то странное? Заманчиво отредактировать этот user_id, чтобы устранить путаницу ..
MBHNYC
Нет, извините, было неясно, идентификатор компании - это то, что дублируется. Пользователь уже существует и в данном случае является постоянным. На самом деле это проблема наследия, с которой мне приходится иметь дело. Буду редактировать пост с дополнительной информацией
D-Nice
Вам не нужно воссоздавать таблицу. Просто посмотрите мой ответ, чтобы сбросить последовательность.
Dondi Michael Stroma

Ответы:

88

Rails, вероятно, использует встроенную последовательность PostgreSQL. Идея последовательности заключается в том, что она используется только один раз.

Самое простое решение - установить для столбца company.id наибольшее значение в таблице с помощью следующего запроса:

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

Я предполагаю, что ваше имя последовательности "company_id_seq", имя таблицы "company" и имя столбца "id" ... замените их на правильные. Вы можете получить имя последовательности SELECT pg_get_serial_sequence('tablename', 'columname');или посмотреть определение таблицы с помощью \d tablename.

Альтернативное решение - переопределить метод save () в классе вашей компании, чтобы вручную установить идентификатор компании для новых строк перед сохранением.

Донди Майкл Строма
источник
Я предполагаю, что это будет делать, если автоматическое назначение начинается с того, что в настоящее время является самым высоким значением + 1?
D-Nice
Я думаю, что это лучший ответ на мой вопрос, однако, по не связанным с этим причинам, мне придется найти способ использовать стратегию, описанную в моем редактировании OP
D-Nice
Я не понимаю, почему это случилось с самого начала? Это случилось со мной, и я хотел бы понять, как это вообще возможно.
Hunt Burdick
2
@Websitescenes, если в PostgreSQL есть столбец SERIAL (последовательный столбец - это столбец, в котором значение по умолчанию является следующим значением в последовательности), а затем заполняет таблицу жесткими значениями в этом столбце, последовательность не будет обновляться автоматически. Пример: 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.
Донди Майкл Строма
207

Я сделал это, и проблема была решена.

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

Я нашел reset_pk_sequence! из этой ветки. http://www.ruby-forum.com/topic/64428

Пирог
источник
4
Спасибо, лучшее решение. После переноса базы данных у меня возникла такая же проблема.
Олег Пасько
63
Или однострочный эквивалент (для целей копирования / вставки консоли rails):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf
Есть идеи, как это рассинхронизируется?
Высокий Пол
26

На основе ответа @Apie .

Вы можете создать задачу и запустить ее, когда вам нужно:

rake database:correction_seq_id

Вы создаете такие задачи:

rails g task database correction_seq_id

И в файле created ( lib/tasks/database.rake) поместите:

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
inye
источник
4

Для меня это проблема с базой данных, а не проблема с Rails. Возможно ли, что ваша база данных имеет неправильное начальное значение идентификатора в idстолбце? Чтобы проверить, попробуйте сделать пару вставок прямо в вашу базу данных и посмотрите, существует ли такое же поведение.

mynameiscoffey
источник
3
Почему голос против? Это точное поведение, которое происходит, если вы устанавливаете свою последовательность приращения на что-то меньшее, чем другие существующие значения, и поэтому иногда сталкиваются с конфликтами при вставке данных. В плакате уже говорилось, что есть данные, которые попадают в этот случай.
mynameiscoffey
Могу вставить нормально. После того, как я получу эту ошибку, я могу снова запустить то же действие и заставить его работать, если следующий идентификатор в последовательности еще не принят.
D-Nice
похоже, что это была моя ситуация - я столкнулся с проблемой, но следующая запись, которую я вставил, работала нормально, поэтому она, должно быть, попала в нужное место.
Бен Уиллер
4

Я решил эту проблему, выполнив следующую команду.

Запустите это в консоли рельсов

ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Джигар Бхатт
источник