Я могу познакомить тебя с Арелем. ПРИМЕЧАНИЕ : Мой код заканчивает тем, что вызывает за кулисами два запроса, которые я объясню.
Недавно мне пришлось самому разрабатывать ЛЕВЫЕ СОЕДИНЕНИЯ в ARel.Лучшее, что вы можете сделать, играя с ARel, - запустить консоль Rails или сеанс IRB и запустить метод #to_sql на ваших объектах ARel, чтобы увидеть, какой SQL они представляют.Делайте это рано и часто!
Вот ваш SQL, немного исправленный для согласованности:
SELECT *
FROM records
LEFT OUTER JOIN other_records ON other_records.record_id = records.id
AND other_records.some_date > now()
WHERE other_records.something = 'another'
Я предполагаю, что ваша records
модель - Record, а other_records
- OtherRecord.В переводе на ARel и ActiveRecord:
class Record < ActiveRecord::Base
# Named scope that LEFT JOINs OtherRecords with some_date in the future
def left_join_other_in_future
# Handy ARel aliases
records = Record.arel_table
other = OtherRecord.arel_table
# Join criteria
record_owns_other = other[:record_id].eq(records[:id])
other_in_future = other[:some_date].gt(Time.now)
# ARel's #join method lets you specify the join node type. Defaults to InnerJoin.
# The #join_sources method extracts the ARel join node. You can plug that node
# into ActiveRecord's #joins method. If you call #to_sql on the join node,
# you'll get 'LEFT OUTER JOIN other_records ...'
left_join_other = records.join(other, Arel::Nodes::OuterJoin).
on(record_owns_other.and(other_in_future)).
join_sources
# Pull it together back in regular ActiveRecord and eager-load OtherRecords.
joins(left_join_other).includes(:other_records)
end
end
# MEANWHILE...
# Elsewhere in your app
Record.left_join_other_in_future.where(other_records: {something: 'another'})
Я включил объединение в именованную область, поэтому вам не нужно смешивать все эти ARel с логикой вашего приложения.
Мой ARel заканчиваетсядо вызова двух запросов за кулисами: первый выбирает записи, используя критерии JOIN и WHERE, второй выбирает все OtherRecords «WHERE other_records.record_id IN (...)», используя большой список всех идентификаторов записей из первого запроса.
Record.includes()
определенно дает вам левостороннее соединение, которое вы хотите, но я не знаю, как внедрить ваши собственные критерии в объединение.Вы можете использовать Record.joins()
вместо ARel, если вы хотите написать SQL самостоятельно:
Record.joins('LEFT OUTER JOIN other_records' +
' ON other_records.record_id = records.id' +
' AND other_records.some_date > NOW()')
Я действительно, действительно предпочитаю, чтобы адаптер базы данных писал мой SQL, поэтому я использовал ARel.
Если бы это был я, я бы подумал о включении дополнительного критерия соединения в предложение WHERE.Я предполагаю, что вы спрашиваете, потому что добавление дополнительного критерия к объединению делает EXPLAIN запроса более привлекательным или потому что вы не хотите иметь дело с NULL в столбце other_records.some_date, когда нет других связанных с другими other_records.