【问题标题】:Ruby on Rails: Left Outer Join with SortRuby on Rails:带排序的左外连接
【发布时间】:2017-02-07 19:11:38
【问题描述】:

我有一个用于发送电子邮件的 RoR 应用程序,我有模型 Contacts、Recipients 和 Users,其中一个用户可以有多个联系人,一个联系人可以有多个收件人。数据如下所示:

 User
 ID     Name
 1      Ben
 2      Emily
 3      Brian

 Contact
 ID       Name     User_ID
 1        Jack     1
 2        Joe      2
 3        Chloe    2
 4        Jacob    1
 5        Trevor   3

 Recipient
 ID     Contact_ID     Email_ID (not relevant for this question)
 1      1              2
 2      1              1
 3      4              5
 4      1              6
 5      2              8

我要做的是在联系人的索引页面上,显示属于用户的所有联系人,允许用户选择按什么排序,例如姓名和收到的电子邮件数量。

我遇到的问题是我可以毫无问题地显示属于用户的所有联系人,但我很难让用户按他们收到的电子邮件数量对联系人进行排序。他们收到的电子邮件数量可以通过计算具有相同联系人 ID 的收件人数量来计算。我遇到此问题的原因是因为我想显示属于用户的所有联系人,而不管他们是否收到电子邮件(联系人可能已收到 0 封电子邮件)。我试过的代码是:

@contacts = Contact.where(user_id: session[:user_id]).all.order(params.fetch(:sort, 'id asc'))

但这不允许用户按联系人收到的电子邮件数量进行排序。

我也试过这个:

  @contacts = Contact.where(user_id: session[:user_id]).joins(:recipients).group('contacts.id').all.order(params.fetch(:sort, 'id asc'))

但这只会显示已收到电子邮件的联系人。

我也试过这个:

@contacts = Contact.where(user_id: session[:user_id]).joins("LEFT OUTER JOIN contacts ON contacts.id = recipients.contact_id").all.order('max(recipients.contact_id) DESC')

这会显示所有已收到和未收到电子邮件的联系人并允许排序,但会显示重复数据。 IE。使用上述数据,如果 Ben 查看他的联系人(user_id 1),他可以看到 Jack 和 Jacob,但由于 Jack 收到了 3 封电子邮件,他会看到以下内容:

First Name:
Jack
Jacob
Jack
Jack
Jack

有没有办法解决这个问题?请注意,我目前在index.html.erb 上使用以下代码,以允许用户选择他们想要通过什么来订购记录:

<form>
    Order By:
    <select name="sort">
        <option value="firstname ASC">First Name (A - Z)</option>
        <option value="firstname DESC">First Name (Z - A)</option>
        <option value="surname ASC">Surname (A - Z)</option>
        <option value="surname DESC">Surname (Z - A)</option>
        <option value="email ASC">Email (A - Z)</option>
        <option value="email DESC">Email (Z - A)</option>
        <option value="max(recipients.contact_id) ASC">Emails Recieved (Low - High)</option>
        <option value="max(recipients.contact_id) DESC">Emails Recieved (High - Low)</option>

        <option value="updated_at ASC">Date Updated (Oldest First)</option>
        <option value="updated_at DESC">Date Updated (Newest First)</option>
    </select>
    <button>
        Go
    </button>
</form>

【问题讨论】:

  • 您需要汇总(阅读计数)收到的电子邮件数量,例如select('count(recipients.id) as emails_received').group('contacts.id')
  • 这将如何让用户订购它们?
  • 你的意思是order('count(recipients.id) ASC')?那么就这样
  • @engineersmnky 我相信您需要在select 中拥有count 才能做到这一点。
  • @engineersmnky 那么整行会是什么样子? @contacts = Contact.where(user_id: session[:user_id]).select('count(recipients.id) as emails_received').group('contacts.id').all.order('count(recipients.id) ASC') ?

标签: sql ruby-on-rails ruby ruby-on-rails-3 join


【解决方案1】:

为了进行 LEFT OUTER JOIN,试试这个:

Contact.includes(:recipients).references(:recipients)

注意:这也会预加载收件人并向查询中添加大量 SQL

如果您还想进行排序,那么您应该可以这样做:

Contact.includes(:recipients).references(:recipients).group("contacts.id, recipients.id").order("COUNT(recipients.id) ASC")

【讨论】:

  • 这似乎有效,但我无法让订购工作?
  • @BenSmith 我还添加了一个关于如何进行排序的示例。我已经在 Postgres 上测试了这样一个有效的查询。
  • 太好了!谢谢!
【解决方案2】:

试试这个。

@contacts = Contact.select('contacts.*, count(recipients.id) as emails_received')
                   .where(user_id: session[:user_id])
                   .joins(:recipients)
                   .group('contacts.id')
                   .order('emails_received ASC')

如果你想显示那些没有收到邮件的,你需要左外连接。

@contacts = Contact.select('contacts.*, count(recipients.id) as emails_received')
                   .where(user_id: session[:user_id])
                   .joins('LEFT OUTER JOIN recipients ON recipients.contact_id = contacts.id)
                   .group('contacts.id')
                   .order('emails_received ASC')

【讨论】:

  • 谢谢,但这给了我错误:SQLite3::SQLException: no such column: reci‌​pients.id: SELECT contacts.*, count(recipients.id) as emails_received FROM "contacts" INNER JOIN "recipients" ON "recipients"."contact_id" = "contacts"."id" WHERE "contacts"."user_id" = ? GROUP BY contacts.id ORDER BY count(reci‌​pients.id) ASC
  • 我删除了.order('count(reci‌​pients.id) ASC'),看看这是否会显示所有联系人。不幸的是,它不会显示未收到电子邮件的联系人 - 所以那些没有出现在收件人表中的联系人。
  • @BenSmith 哎呀......我的错。忘记改变了。应该是.order('emails_received ASC')
猜你喜欢
  • 2021-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 2016-03-22
  • 1970-01-01
  • 1970-01-01
  • 2012-06-18
相关资源
最近更新 更多