【问题标题】:Include all objects but apply conditions only on associations in ActiveRecord包括所有对象,但仅对 ActiveRecord 中的关联应用条件
【发布时间】:2017-08-23 04:57:23
【问题描述】:

以下型号,

class User < ActiveRecord::Base
  has_many :addresses
  # Has an attribute named active
end

class Address < ActiveRecord::Base
  # Has an attribute named city
end

如何在对 DB 的单个查询中获取所有 users where active: true 并为每个用户仅包含 addresses where city: Foo

注意:如果用户没有city 为“Foo”的地址,则他们仍必须包含在结果中,addresses 为空。

示例数据

test=# select * from users;
 id | name | active 
----+------+--------
  1 | Jack | t
  2 | John | t
  3 | Jane | t
  4 | Jill | f
(4 rows)

test=# select * from addresses;
 id | user_id |    street    | city 
----+---------+--------------+------
  1 |       1 | Foo Street   | Foo
  2 |       1 | Bar Street   | Bar
  3 |       2 | Bar Street   | Bar
  4 |       4 | Foo Street   | Foo
  5 |       1 | Foo Street 1 | Foo
(5 rows)

预期结果

 id | name |    street    | city 
----+------+--------------+------
  1 | Jack | Foo Street 1 | Foo
  1 | Jack | Foo Street   | Foo
  2 | John |              | 
  3 | Jane |              | 
(4 rows)

下面的 SQL 查询可以产生该结果,但我想不出一种方法让 AR 生成该查询或任何其他查询来产生上述结果。

select users.id, name, street, city from users left join lateral (
  select * from addresses where addresses.user_id = users.id and city = 'Foo'
) addr on true
where users.active;

【问题讨论】:

  • 这是User.includes(:addresses).where(active: true).where(addresses: {city: 'Foo'}) 你在找什么吗?
  • 排除在“Foo”中没有任何地址的用户。
  • 您的要求不是很清楚,各种答案都不能产生您想要得到的结果。考虑展示一些具有各种地址组合的用户记录的示例,并显示所需的过滤结果。
  • 添加了一些例子。

标签: ruby-on-rails ruby ruby-on-rails-4 rails-activerecord


【解决方案1】:

请尝试 添加Address模型

class Address < ActiveRecord::Base
  belongs_to :user  
end

那么你的查询就像

User.joins("LEFT JOIN addresses ON users.id=addresses.user_id").where("users.active=? AND addresses.city=?", true, "FOO")

【讨论】:

  • 不,这不是我们想要的。它排除了没有任何address where city: Foo 并且需要单独查询以获取匹配地址的属性的用户。
  • 试试Inner JoinUser.joins(:addresses)
  • 这将排除根本没有任何地址但实际上没有任何改变的用户。 :)
  • :) 试试 .where("users.active=?ANDaddresses.city=?", true, "FOO")
  • 这与 Pavan 在直接评论问题时的建议有何不同?
【解决方案2】:

试试这个

User.joins("LEFT JOIN addresses on addresses.user_id = users.id").where(active: true).select("users.*,if (addresses.city = 'Foo', addresses.city, '') AS 'city',if (addresses.city = 'Foo', addresses.street, '') AS 'street'").order("city").group(:id).uniq(:id)

【讨论】:

  • 这不包括地址位于“Foo”以外城市的用户。
  • 我已经用示例数据和结果更新了问题。希望这可以澄清事情。
  • 它首先返回没有任何地址的User 对象,当我尝试访问addresses 属性时,它会为每个用户触发一个查询,并在不应用城市条件的情况下包含所有地址。
  • 您不需要通过user.addresses 访问addressesaddress 已经是joinuser 表。可以直接访问user.cityuser.street
【解决方案3】:

我想可以通过这种方式实现,我希望它可能是正确的:-

User.all.select{|a|a.active == true}.map(&:address).flatten.select{|v|v.city == "Foo" || v.city == ""}

认为可以对其进行修改以获得正确的结果。希望它有帮助,如果我出错了,请纠正我。

【讨论】:

    猜你喜欢
    • 2011-12-10
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多