Взяться за первое исполнение в Ruby on Rails

Это вопрос о методах запросов ActiveRecord:

  • first Найдите первую запись (или первые N записей, если задан параметр). Если порядок не задан, он будет выполнять первичный ключ.
  • take Дает запись (или N записей, если предоставляется параметр) без какого-либо подразумеваемого порядка. Порядок будет зависеть от реализации базы данных. Если заказ будет предоставлен, он будет соблюден.

UseCase: извлекать запись из базы данных на основе уникального атрибута, например.

User.where(email: '[email protected]')

здесь, first генерирует

SELECT "users".* FROM "users" WHERE "users"."email" = '[email protected]' ORDER BY "users"."id"` ASC LIMIT 1

take генерирует

SELECT "users".* FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1

как показано выше first, добавляется дополнительное предложение заказа. Мне интересно, есть ли разница в производительности между take vs first.

Является take быстрее, чем first или наоборот?

Ответы

Ответ 1

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

Степень, в которой она быстрее, будет меняться в зависимости от:

  • Сколько времени сохраняется, если вам не нужно искать несколько строк. В худшем случае здесь требуется полное сканирование большой таблицы, но одна соответствующая строка найдена очень рано в сканировании. "take" позволит остановить сканирование.

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

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

Во многих случаях, когда для их поиска очень мало совпадающих записей и доступа на основе индексов, вы обнаружите, что разница тривиальна (там, где в вашем примере есть уникальный индекс в "электронной почте" ). Тем не менее, я бы по-прежнему использовал "взять", предпочитая сначала даже тогда.

Изменить: я просто добавлю, хотя это немного не по теме, что в вашем примере вы также можете использовать:

User.find_by(email: '[email protected]')

Сгенерированный запрос должен быть таким же, как и для take, но семантика немного ясна.