查找具有至少一个关联的记录,但排除符合任何条件的记录

时间:2018-11-20 00:47:48

标签: ruby-on-rails postgresql activerecord

在以下设置中,客户通过标记具有许多标记。

class Customer
  has_many :taggings
  has_many :tags, through: :taggings
end

class Tagging
  belongs_to :tag
  belongs_to :customer
end

我试图在Postgres的Rails中执行的查询是查找具有至少一个标签但没有标签A或B的所有客户。

由于有成千上万的客户,因此需要考虑性能。

3 个答案:

答案 0 :(得分:0)

获取标签A和B的ID

ids_of_tag_a_and_b = [Tag.find_by_title('A').id, Tag.find_by_title('B').id]

查找具有至少一个标签但没有标签A或B的所有客户。

#Customer.joins(:tags).where.not("tags.id in (?)", ids_of_tag_a_and_b)
Customer.joins(:tags).where.not("tags.id = ? OR tags.id = ?", tag_id_1, tag_id_2)

答案 1 :(得分:0)

tag_ids是A和B ID的数组:

tag_ids = [a.id, b.id]

然后,您需要找到带有A或B标签的客户:

except_relation = Customer.
  joins(:tags).
  where(tags: { id: tag_ids }).
  distinct

并将它们从具有至少一个标签的标签中排除:

Customer.
  joins(:tags).
  where.not(id: except_relation).
  distinct
INNER JOIN产生的

.joins删除了没有标签的客户,并且是复制的来源,因此需要distinct

UPD :当需要性能时,可能必须更改数据库架构,以避免额外的联接和索引。 您可以搜索jsonb标签实现的示例。

答案 2 :(得分:0)

请尝试以下查询。

Customer.distinct.joins(:taggings).where.not(id: Customer.joins(:taggings).where(taggings: {tag_id: [tag_id_a,tag_id_b]}).distinct )

说明。

联接将触发内部联接查询,并确保您仅获得那些至少具有与其关联的标签的客户。

where.not将照顾您的其他状况。

希望这会有所帮助。