Существует ли компактный способ с ActiveRecord запрашивать, какой идентификатор он будет использовать следующим, если объект будет сохранен в базе данных? В SQL такой запрос будет выглядеть примерно так:
SELECT max(id) + 1 FROM some_table;
Существует ли компактный способ с ActiveRecord запрашивать, какой идентификатор он будет использовать следующим, если объект будет сохранен в базе данных? В SQL такой запрос будет выглядеть примерно так:
SELECT max(id) + 1 FROM some_table;
Думайте, что это должно работать:
YourModel.last.id + 1
Ниже приведена небольшая версия Taryn East:
Model.maximum(:id).next
Принимая фиговый ответ, я мог бы обратить ваше внимание на небольшую вещь. Если вы доберете следующий ID до определенной записи перед сохранением, я думаю, что это не очень хорошая идея.
поскольку в качестве примера в веб-системе
Я не уверен, что вы хотите, чтобы последний id делал то, что я здесь думаю, но если это так, просто обратите ваше внимание.
Если ваша база данных - 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
Чуть лучше, чем принятый ответ:
YourModel.maximum(:id) + 1
По-прежнему подвержен условиям гонки и т.д., но по крайней мере он будет учитывать пропущенные идентификаторы и немного более эффективен, чем, скажем, упорядочение таблицы по id, а затем возвращение последнего.
Это старый вопрос, но ни один из других ответов не работает, если вы удалили последнюю запись:
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
Если вы хотите быть уверенным, что никто другой не может принять "новый" индекс, вы должны заблокировать таблицу. Используя что-то вроде:
ActiveRecord::Base.connection.execute("LOCK TABLES table_name WRITE")
и
ActiveRecord::Base.connection.execute("UNLOCK TABLES")
Но это специфично для каждого механизма базы данных.
Единственный правильный ответ для столбца последовательного идентификатора:
YourModel.maximum(:id)+1
Если вы сортируете свою модель по умолчанию, последняя и первая будут зависеть от этого порядка.
Если никто другой не использует таблицу (иначе вам придется использовать блокировку), вы можете получить значение автоинкремента из 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]
Получить наибольший идентификатор в таблице
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
Я не думаю, что существует общий ответ на этот вопрос, поскольку вы можете не захотеть считать одну базу данных другой. Oracle, например, может присваивать идентификатор последовательности или, что еще хуже, с помощью триггера.
Что касается других баз данных, можно установить их с помощью несекретного или произвольного выделения идентификаторов.
Могу я спросить, что такое прецедент? Зачем вам нужно ожидать следующего идентификатора? Что означает "следующий"? Относительно того, когда? Что относительно условий гонки (многопользовательская среда)?
Кажется, что здесь нужен ответ, который устраняет условия гонки и решает актуальную проблему предопределения уникальных идентификаторов.
Чтобы решить эту проблему, вы поддерживаете вторую модель, которая служит уникальному идентификатору для основной модели. Таким образом, у вас нет условий гонки.
Если вам нужно сделать эти идентификаторы безопасными, вы должны использовать их с SHA256 (или SHA512) и сохранить хеш в качестве индексированного столбца в модели идентификатора при их создании.
Затем они могут быть связаны и проверены при использовании в первичной модели. Если вы не используете их, вы все равно связываете их, чтобы обеспечить проверку.
Я немного позже отправлю код примера.
Не понимайте намерения, но вы можете подумать об использовании GUID
Вы никогда не должны предполагать, каким будет следующий идентификатор в последовательности. Если у вас более 1 пользователя, вы рискуете использовать идентификатор к моменту создания нового объекта.
Вместо этого безопасным подходом будет создание нового объекта и его обновление. 2 попадания в вашу базу данных, но с абсолютным идентификатором объекта, с которым вы работаете.
Этот метод убирает догадки из уравнения.
Я пришел к этому такому вопросу, потому что я хотел иметь возможность предсказать идентификатор модели, созданной в моем наборе тестов (идентификатор был затем использован для запроса 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 }
Хотя это несколько странно (и немного неэффективно для вашей базы данных, поскольку вы создаете дополнительный объект ради его идентификатора), оно не делает ничего глупого для транзакции и надежно работает в среде тестирования без записей (если только ваши тесты не выполняются в параллельно с доступом к одному и тому же БД... в этом случае: условия гонки... может быть?).