Rails автоматично призначає ідентифікатор, який уже існує


92

Я створюю новий запис так:

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, з автоматично присвоєними ідентифікаторами та підтримкою всіх існуючих зв’язків?


чому ви не дозволяєте рейкам автоматично призначати ідентифікатор? Це усуне будь-яку небезпеку дублювання - чи це проблема зі застарілими даними, де ви повинні зберігати старі посвідчення особи? Просто хочу трохи зрозуміти бізнес-кейс, оскільки при створенні нового об’єкта це не так, як зазвичай.
MBHNYC

@MBHNYC Я думаю, що D-Nice призначає user_id під час створення компанії, а не ідентифікатор, як ви думаєте (і я це зробив на мить).
Аніл,

Ооо, хороший улов Аніл - ви абсолютно праві. @ D-Nice, можливо, додайте міграцію для цієї таблиці до свого повідомлення, якщо буде щось дивне? Спокуса редагувати цей user_id, щоб усунути плутанину ..
MBHNYC

Ні, вибачте, незрозуміло, ідентифікатор компанії - це те, що дублюється. Користувач уже існує і в цьому випадку є константою. Насправді це проблема спадщини, з якою мені доводиться мати справу. Відредагує публікацію з додатковою інформацією
D-Nice

Не потрібно відтворювати таблицю. Просто перегляньте мою відповідь, щоб скинути послідовність.
Донді Майкл Строма

Відповіді:


87

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,

Я не розумію, чому це сталося з самого початку? Це сталося зі мною, і я хотів би зрозуміти, як це взагалі можливо.
Хант Бурдік

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.
Dondi Michael Stroma

206

Я зробив це, що вирішило проблему для мене.

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
Дякую, найкраще рішення. Після передачі бази даних у мене була та ж проблема.
Олег Пасько

62
Або однолінійний еквівалент (для копіювання / вставки консолі рейок):ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.reset_pk_sequence!(t) }
Raf

Будь-яка ідея, як це виходить з синхронізації?
Високий Павло

25

На основі відповіді @Apie .

Ви можете створити завдання та запустити, коли вам потрібно, за допомогою:

rake database:correction_seq_id

Ви створюєте такі завдання:

rails g task database correction_seq_id

А у файл generated ( 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

4

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


3
Чому голос проти? Це саме та поведінка, яка трапляється, якщо ви встановите послідовність приросту на щось, що нижче за інші існуючі значення, і, отже, час від часу потрапляє на зіткнення під час вставки даних. Плакат вже сказав, що існують дані, які потрапляють у цю справу.
mynameiscoffey

Я можу вставити штраф. Після того, як я отримаю цю помилку, я дійсно можу виконати ту саму дію ще раз і змусити її працювати, якщо наступний ідентифікатор у послідовності ще не зроблений.
D-Nice

здається, це була моя ситуація - я зіткнувся з проблемою, але наступний запис, який я вставив, спрацював нормально, тому він, мабуть, привів насіння в потрібне місце.
Ben Wheeler

3

Я вирішив цю проблему, дотримуючись команди.

Запустіть це на своїй консолі рейок

ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.