Спасибо за все ваши предложения. Я проголосовал за них, а не принял их, потому что я не чувствую, что они вполне охватывают то, что я хочу, но то, что я собираюсь сделать, безусловно, принесло им пользу.
Я все еще чувствую, что по возможности следует избегать SQL (по всем причинам, которые я уже упоминал в комментариях), но я думаю, что в этом случае это невозможно, поэтому я решил определить именованную область видимости для Модель участника, как это:
РЕДАКТИРОВАТЬ: Первоначально я определил это как область по умолчанию - однако я решил прислушаться к предупреждению от KandadaBoggu для сложных областей видимости по умолчанию, поэтому вместо этого сделал его именованной областью. Запрос также немного сложнее, чем другие, описанные, чтобы справиться с исключением возобновленных членств (где дата начала в будущем), когда существует действующее в данный момент членство. Еще раз спасибо KandadaBoggu за кости запроса и подсказку о том, как избежать выбора N + 1.
scope :with_membership, lambda {
select('members.*, m.applied AS applied, m.paid AS paid, m.start AS start, m.expiry as expiry').
joins("INNER JOIN (
SELECT m3.*
FROM memberships m3
LEFT OUTER JOIN memberships m5
ON m3.member_id = m5.member_id
AND m5.created_at > m3.created_at
AND m5.expiry > #{sanitize(Time.now)}
WHERE m3.expiry > #{sanitize(Time.now)}
AND m5.id IS NULL
UNION
SELECT m1.*
FROM memberships m1
LEFT OUTER JOIN memberships m2
ON m1.member_id = m2.member_id
AND m2.created_at > m1.created_at
LEFT OUTER JOIN memberships m4
ON m1.member_id = m4.member_id
AND m4.expiry > #{sanitize(Time.now)}
WHERE m2.id IS NULL AND m4.id IS NULL
) m
on (m.member_id = members.id)") }
Я видел более красивые биты кода. Но мое объяснение таково: если вам понадобится ужасный SQL-код, как этот, вы могли бы иметь его только в одном месте, а не повторять повсюду для каждого другого запроса, который вам может понадобиться (например, с истекшим сроком действия). участники, участники, которые еще не заплатили и т. д. и т. д.).
Имея вышеуказанную область действия, я могу просто обрабатывать дополнительные столбцы членства как обычные столбцы в Member
и писать довольно простые запросы, например:
#expired
Member.with_membership.find :all, :conditions => ['expires < ?', Time.now ]
#current
Member.with_membership.find :all, :conditions => ['started < ? AND expires > ?', Time.now, Time.now ]
#pending payment
Member.with_membership.find :all, :conditions => ['applied < ? AND paid IS NULL', Time.now ]
Чтобы кратко обосновать принятие моего собственного ответа, а не одного из других, очень полезных ответов, я хочу отметить, что никогда не было вопроса о том, как получить «наибольшее n на группу» (хотя это является компонентом Это). Речь шла о том, как наилучшим образом справиться с такого рода запросами в конкретной среде Rails, с конкретной проблемой членов с несколькими членствами, в которых одновременно активен только один, и с рядом похожих запросов, для которых всем нужны поля из это единственное активное членство.
Вот почему я думаю, что использование именованной области видимости, таким образом, является, в конечном счете, лучшим ответом на вопрос. Смирись с отвратительным SQL-запросом, но держи его только в одном месте.