Существует несколько подходов для этого.Несмотря на то, что 4000 записей не так уж много в схеме вещей, избегать необходимости посещать несколько запросов (как вы намекнули в своем вопросе) - это «хорошая вещь».
Ключом к решению являетсякак вы выбираете, какой адрес вы хотите посмотреть.«Первый» является относительным и недетерминированным без квалификатора (упорядочить по или каким-либо другим атрибутом).
Простой подход - ассоциация has_one
Один простой подход заключается в определении отношения has_one, например
class User
has_many :addresses
has_one :primary_address, :class_name => 'Address', :conditions => ["primary = ?", true]
end
При условии, что у вас есть атрибут в адресе, вы можете использовать решающий элемент.Если вы не укажете условие, вы получите только то, что вводится адресом «первым», так как has_one применяет SQL LIMIT 1. к базовому запросу.
Это упрощает сбор пользователей и одного запроса.сведения об адресе:
# iterating over users
User.scoped.each { |user| puts user.primary_address.area_code }
# or collect area_codes
area_codes = User.scoped.collect{ |user| user.primary_address.area_code }
Тем не менее, он все еще посещает каждую строку, поэтому для каждого пользователя будет добавлен дополнительный запрос (и обработка массива ruby).
Намного лучше - Арель
После того, как вы установили критерии выбора между несколькими адресами, которые может иметь пользователь, можно свернуть это в один запрос.
Если я предполагаю, что мы просто выберем адрес поmin (id) - и при условии, что вы находитесь на Rails 3 - вот подход Arel для запроса различных кодов области:
address = Address.arel_table
primary_addresses = Address.where(
address[:id].in(
address.group(address[:user_id]).project(address[:id].minimum)
)
)
primary_addresses - это область, которая дает вам полный набор «первичных» адресов дляпользователи.Затем вы можете обработать / составить список / собрать, как вам нужно, например:
area_codes = primary_addresses.collect(&:area_code)
Запрос фактически сгенерирует SQL следующим образом:
SELECT "addresses".*
FROM "addresses"
WHERE "addresses"."id" IN (
SELECT MAX("addresses"."id") AS max_id FROM "addresses" GROUP BY "addresses"."user_id"
)
Как видите, суб-выбор возвращаетсясписок идентификаторов адресов - по одному для каждого пользователя.Если вы хотите использовать другой критерий для выбора используемого адреса, вы можете изменить подзапрос (например, чтобы он был выбран по первичному = true с пределом 1)