Подтвердить что ты не робот

Как получить ActiveRecord для отображения следующего идентификатора (последний + 1) в Ruby on Rails?

Существует ли компактный способ с ActiveRecord запрашивать, какой идентификатор он будет использовать следующим, если объект будет сохранен в базе данных? В SQL такой запрос будет выглядеть примерно так:

SELECT max(id) + 1 FROM some_table;
4b9b3361

Ответ 1

Думайте, что это должно работать:

YourModel.last.id + 1

Ответ 2

Ниже приведена небольшая версия Taryn East:

Model.maximum(:id).next

Ответ 3

Принимая фиговый ответ, я мог бы обратить ваше внимание на небольшую вещь. Если вы доберете следующий ID до определенной записи перед сохранением, я думаю, что это не очень хорошая идея.

поскольку в качестве примера в веб-системе

  • вы получите последний id как 10
  • вы устанавливаете следующий id как 11
  • перед сохранением записи кто-то еще сохранил запись, теперь   последний идентификатор должен быть равен 12.

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

Ответ 4

Если ваша база данных - Postgres, вы можете получить следующий идентификатор с этим (пример для таблицы с именем 'users'):

ActiveRecord::Base.connection.execute("select last_value from users_id_seq").first["last_value"]

В отличие от других ответов, это значение не зависит от удаления записей.

Там, вероятно, эквивалент MySQL, но я не настроен для подтверждения.

Если вы импортировали данные в базу данных postgresql, есть большая вероятность, что для следующего значения идентификатора после импорта не будет установлено следующее целое число, большее, чем наибольшее импортированное вами. Таким образом, вы столкнетесь с проблемами, пытаясь сохранить экземпляры модели activerecord.

В этом случае вам нужно будет вручную установить следующее значение идентификатора:

ActiveRecord::Base.connection.execute("alter sequence users_id_seq restart with 54321;") #or whatever value you need

Ответ 5

Чуть лучше, чем принятый ответ:

YourModel.maximum(:id) + 1

По-прежнему подвержен условиям гонки и т.д., но по крайней мере он будет учитывать пропущенные идентификаторы и немного более эффективен, чем, скажем, упорядочение таблицы по id, а затем возвращение последнего.

Ответ 6

Это старый вопрос, но ни один из других ответов не работает, если вы удалили последнюю запись:

Model.last.id #=> 10
Model.last.destroy
Model.last.id #=> 9, so (Model.last.id + 1) would be 10... but...
Model.create  #=> 11, your next id was actually 11

Я решил проблему, используя следующий подход:

current_value = ActiveRecord::Base.connection.execute("SELECT currval('models_id_seq')").first['currval'].to_i
Model.last.id #=> 10
Model.last.destroy
Model.last.id #=> 9
current_value + 1 #=> 11

Ответ 7

Если вы хотите быть уверенным, что никто другой не может принять "новый" индекс, вы должны заблокировать таблицу. Используя что-то вроде:

ActiveRecord::Base.connection.execute("LOCK TABLES table_name WRITE")

и

ActiveRecord::Base.connection.execute("UNLOCK TABLES")

Но это специфично для каждого механизма базы данных.

Единственный правильный ответ для столбца последовательного идентификатора:

YourModel.maximum(:id)+1

Если вы сортируете свою модель по умолчанию, последняя и первая будут зависеть от этого порядка.

Ответ 8

Если никто другой не использует таблицу (иначе вам придется использовать блокировку), вы можете получить значение автоинкремента из MySQL, команда SQL

SELECT auto_increment FROM information_schema.tables 
WHERE table_schema = 'db_name' AND table_name = 'table_name';

и команда Rails будет

ActiveRecord::Base.connection.execute("SELECT auto_increment 
     FROM information_schema.tables 
     WHERE table_schema = 'db_name' AND table_name = 'table_name';").first[0]

Ответ 9

Получить наибольший идентификатор в таблице

YourModel.maximum(:id)

Запустится следующий sql

SELECT MAX("your_models"."id") AS max_id FROM "your_models"

Преобразуйте результат в целое число, вызвав to_i. Это важно, так как для пустой таблицы приведенная выше максимальная команда вернет nil. К счастью, nil.to_i возвращает 0.

Теперь, чтобы получить следующий доступный id, просто добавьте 1 + 1`

Конечный результат:

YourModal.maximum(:id).to_i+1

Ответ 10

Я не думаю, что существует общий ответ на этот вопрос, поскольку вы можете не захотеть считать одну базу данных другой. Oracle, например, может присваивать идентификатор последовательности или, что еще хуже, с помощью триггера.

Что касается других баз данных, можно установить их с помощью несекретного или произвольного выделения идентификаторов.

Могу я спросить, что такое прецедент? Зачем вам нужно ожидать следующего идентификатора? Что означает "следующий"? Относительно того, когда? Что относительно условий гонки (многопользовательская среда)?

Ответ 11

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

Чтобы решить эту проблему, вы поддерживаете вторую модель, которая служит уникальному идентификатору для основной модели. Таким образом, у вас нет условий гонки.

Если вам нужно сделать эти идентификаторы безопасными, вы должны использовать их с SHA256 (или SHA512) и сохранить хеш в качестве индексированного столбца в модели идентификатора при их создании.

Затем они могут быть связаны и проверены при использовании в первичной модели. Если вы не используете их, вы все равно связываете их, чтобы обеспечить проверку.

Я немного позже отправлю код примера.

Ответ 12

Не понимайте намерения, но вы можете подумать об использовании GUID

Ответ 13

Вы никогда не должны предполагать, каким будет следующий идентификатор в последовательности. Если у вас более 1 пользователя, вы рискуете использовать идентификатор к моменту создания нового объекта.

Вместо этого безопасным подходом будет создание нового объекта и его обновление. 2 попадания в вашу базу данных, но с абсолютным идентификатором объекта, с которым вы работаете.

Этот метод убирает догадки из уравнения.

Ответ 14

Я пришел к этому такому вопросу, потому что я хотел иметь возможность предсказать идентификатор модели, созданной в моем наборе тестов (идентификатор был затем использован для запроса REST к внешней службе, и мне нужно было предсказать точное значение, чтобы смоделировать запрос),

Я обнаружил, что Model.maximum(:id).next, хотя и элегантен, не работает в среде тестирования рельсов с Model.maximum(:id).next транзакций, так как в базе данных обычно нет записей, поэтому он просто возвращает nil.

Транзакционные фиксации усложняют проблему, поскольку поле автоинкремента увеличивается, даже когда в БД нет записей. Кроме того, использование ALTER TABLE ***your_table_name*** AUTO_INCREMENT = 100 прерывает транзакцию, в которой находятся ваши тесты, потому что она требует своей собственной транзакции.

Чтобы решить эту проблему, я создал новый объект и добавил 1 к его идентификатору:

let!(:my_model_next_id) { FactoryBot.create(:my_model).id + 1 }

Хотя это несколько странно (и немного неэффективно для вашей базы данных, поскольку вы создаете дополнительный объект ради его идентификатора), оно не делает ничего глупого для транзакции и надежно работает в среде тестирования без записей (если только ваши тесты не выполняются в параллельно с доступом к одному и тому же БД... в этом случае: условия гонки... может быть?).