#mysql #sql #ruby-on-rails #ruby
#mysql #sql #ruby-on-rails #ruby
Вопрос:
У меня есть таблица учетных записей и таблица account_logins. Я пытаюсь выбрать каждую учетную запись, а затем упорядочить их по времени последнего входа в систему. Пока у меня есть следующее:
accounts
.confirmed_visible_with_completed_profile
.joins(:logins)
.select('accounts.*, max(account_logins.created_at) as last_sign_in_at')
.group('accounts.id')
Это работает, однако, если с учетной записью не связано account_logins, она не возвращается в запросе. Мой SQL довольно плохой, но я нашел COALESCE через поиск в Google и попытался:
accounts
.confirmed_visible_with_completed_profile
.joins(:logins)
.select('accounts.*, COALESCE(max(account_logins.created_at), 0) as last_sign_in_at')
.group('accounts.id')
Однако это по-прежнему не возвращает записи без связанного account_login . Из того, что я прочитал COALESCE(max(account_logins.created_at), 0)
, должно возвращать 0, если max(account_logins.created_at) равно НУЛЮ, поэтому я не понимаю, почему это не работает. Любая помощь очень ценится
Ответ №1:
Вы пропустили эти значения в части соединения, и поэтому coalesce
в select
этом случае бесполезно. select
применяется ли последняя операция. Вам нужно left join
решить вашу проблему
в rails 5 у вас есть left_joins
accounts
.confirmed_visible_with_completed_profile
.left_joins(:logins)
.select('accounts.*, COALESCE(max(account_logins.created_at), 0) as last_sign_in_at')
.group('accounts.id')
до rails 5
accounts
.confirmed_visible_with_completed_profile
.joins("LEFT JOIN account_logins ON accounts.id = account_logins.account_id")
.select('accounts.*, COALESCE(max(account_logins.created_at), 0) as last_sign_in_at')
.group('accounts.id')
вам все еще нужно coalesce
. с left join
помощью имеет смысл
Комментарии:
1. Спасибо за объяснение — я вижу, что теперь объединения исключат любые записи без account_login, прежде чем произойдет что-либо еще — ответ работает отлично 🙂
2. Мы могли бы немного повеселиться в rails < 5.
.joins(Arel::Nodes::OuterJoin.new(AccountLogin.arel_table,nil).tap { |j| j.right = j.create_on(Account.arel_table[:id].eq(AccountLogin.arel_table[:account_id]))})
, я просто испытываю огромное отвращение к строковым соединениям 🙂3. @engineersmnky я тоже, но я никогда не тратил время на изучение Arel: D
4. @Ursus учитывая ваш четкий опыт работы с ruby и rails, если на то пошло, я бы рекомендовал попытаться найти время, я знаю, что это действительно принесло мне пользу. То, что вы можете сделать,
arel
является удивительным и интегрируется в традиционныеActiveRecord
запросы достаточно плавным образом.5. Спасибо @engineersmnky, я сделаю это 🙂
Ответ №2:
Попробуйте использовать nullif
COALESCE(nullif(max(account_logins.created_at),''), 0)