Есть ли способ быть явным для столбцов .select () при использовании .include () в Rails? - PullRequest
0 голосов
/ 19 ноября 2018

Я пытаюсь оптимизировать мой запрос на использование оперативной памяти, и в таблице, которая находится в .include (: ocr), слишком много столбцов (а также столбец большого размера).Есть ли способ, которым я все еще могу использовать Rails '.include (), но выбрать явные столбцы .select () из таблиц include ()?

Я хочу исключить использование N + 1, что.joins () генерирует.

т.е..

User.select(:email).includes(:ocr => select(:small_value_only))

Ответы [ 4 ]

0 голосов
/ 25 февраля 2019

Я думаю, вы должны создать новую ассоциацию в User, например:

belongs_to :ocr_small_value_only, -> {select('ocr.id,ocr.small_value_only')}, class_name: 'ocr', foreign_key: 'ocr_id'

, а затем выполнить запрос, используя

User.select('ocr_id, email').includes(:ocr_small_value_only)
0 голосов
/ 03 февраля 2019

Если вы действительно пытаетесь получить только столбцы данных, а не полные объекты ActiveRecord, вам следует использовать pluck вместо select. Ускорение такого рода больших запросов - это именно то, для чего был создан pluck. Вместо всех накладных расходов, связанных с созданием всех объектов ActiveRecord для всех ваших данных, pluck просто возвращает массив данных:

values = User.includes(:ocr).pluck(:email, "ocr.small_value_only")
# SELECT "user"."email", ocr.small_value_only FROM "users"
#   LEFT OUTER JOIN "ocrs" ON "ocr"."user_id" = "users"."id"
#
# [["email_1", 3], ["email_2", 7]] # 3 and 7 are the small values

Если вам нужны объекты ActiveRecord, но вы хотите пропустить заполнение некоторых данных, то это то же самое, что и выше, но замените includes на left_joins и pluck на select

values = User.left_joins(:ocr).select(:email, "ocr.small_value_only")
# SELECT "user"."email", ocr.small_value_only FROM "users"
#   LEFT OUTER JOIN "ocrs" ON "ocr"."user_id" = "users"."id"
#
# #<ActiveRecord::Relation [#<user id: 1>, #<user id: 2>]>

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

0 голосов
/ 08 февраля 2019

Я не хочу сказать, что следующие варианты обязательно лучше, чем то, что предлагают Benj и Old Pro , но скорее хочу предоставить еще несколько альтернатив.

Первый использует метод includes, так как это то, что запрашивал OP.Но так как includes по умолчанию выберет все столбцы основной модели (User), сначала нужно удалить этот выбор по умолчанию с помощью , за исключением метода :

User.includes(:ocr).except(:select).select(:id, 'ocr.small_value_only')

OfКонечно, было бы проще использовать join.

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

Наивной реализацией для удаления этих знаний является получение имени таблицы из модели:

User.joins(:ocr).select(:email, "`#{Ocr.table_name}`.`small_value_only`")

Следующая альтернативаполагается на слияние , чтобы избавиться от строки для выбора столбца:

 User.joins(:ocr).merge(Ocr.select(:project_id)).select(:id) 

Но поскольку полученный sql не ссылается на таблицу Ocr для small_value_only, он будет эквивалентензапись:

 User.joins(:ocr).select(:email, :small_value_only) 

и будет работать только до тех пор, пока small_value_only не существует в таблице users.

Последняя альтернатива не имеет этого недостатка, но гораздо болееглаголose:

User.joins(:ocr).select(Ocr.select(:small_value_only).arel.projections).select(:id)
0 голосов
/ 02 февраля 2019

Вы не предоставляете много информации о том, как установлены ваши таблицы, поэтому я предполагаю, что у пользователя belongs_to :ocr, у пользователя есть столбец ocr_id, а имя таблицы ocr - ocr.

Вы должны использовать joins вместо includes, именно так вы генерируете запрос INNER JOIN и сможете получить столбцы столбца объединенной таблицы. Наконец, вы можете присвоить псевдониму столбца значение AS, чтобы его было проще найти в вашей модели:

users = User.joins(:ocr).select(:email, :ocr_id, '`ocr`.`small_value_only` AS `ocr_sma`')

users.each do |user|
  user.email # Users.email for this record
  user.ocr_sma # Joined Ocr.small_value_only for this record
end

(Очевидно, я присвоил ему псевдоним ocr_sma, но вы можете дать ему нужное имя)

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