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

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

SELECT max(id) + 1 FROM some_table;

Ответы [ 13 ]

84 голосов
/ 08 июля 2013

Вот слегка измененная версия Taryn East :

Model.maximum(:id).next
31 голосов
/ 03 мая 2010

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

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

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

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

15 голосов
/ 24 февраля 2016

Если ваша база данных 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
12 голосов
/ 14 мая 2013

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

YourModel.maximum(:id) + 1

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

5 голосов
/ 31 марта 2016

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

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
2 голосов
/ 14 октября 2014

Получить самый большой идентификатор на столе

YourModel.maximum(:id)

Это запустит следующий sql

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

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

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

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

YourModal.maximum(:id).to_i+1
2 голосов
/ 11 апреля 2014

Если никто больше не использует таблицу (в противном случае вам придется использовать блокировку), вы можете получить значение автоинкремента из 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]
2 голосов
/ 26 февраля 2014

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

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

и

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

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

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

YourModel.maximum(:id)+1

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

1 голос
/ 24 сентября 2013

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

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

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

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

Я опубликую пример кода чуть позже.

1 голос
/ 16 февраля 2012

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...