class Editor < ApplicationRecord
  has_many :editor_roles, dependent: :destroy
  has_many :roles, through: :editor_roles
end
class Roles < ApplicationRecord
  has_many :editor_roles, dependent: :destroy
  has_many :editors, through: :editor_roles
end
class EditorRole < ApplicationRecord
  belongs_to :editor
  belongs_to :role
end

问题是:

我在postgres中使用了这个...

Editor.joins(:roles).group('editors.id').having('count(roles) = 0')

但我肯定这是不对的

推荐答案

# TLDR
Editor.left_joins(:roles).where(roles: { id: nil })
Editor.left_joins(:editor_roles).where(id: nil)
# rails 6.1+ https://github.com/rails/rails/pull/34727
Editor.where.missing(:roles)                                     
Editor.where.missing(:editor_roles)

Editor.create([{}, {roles: [Role.create]}, {roles: [Role.create, Role.create]}])

# <Editor:0x00007f63a40962a8 id: 1> # no roles
# <Editor:0x00007f63a409ebb0 id: 2> # 1 role
# <Editor:0x00007f63a5ffa518 id: 3> # 2 roles

要获得具有角色的编辑器,请使用joins.这将过滤掉没有角色的编辑,因此不利于查找缺少的内容.

>> Editor.joins(:roles)                                                                
=> [#<Editor:0x00007f639fad9ee0 id: 2>, 
    #<Editor:0x00007f639fad9d50 id: 3>, 
    #<Editor:0x00007f639fad9c60 id: 3>]

# NOTE: to avoid duplicates use `distinct`
>> Editor.joins(:roles).distinct                                                         
=> [#<Editor:0x00007f63a612bba8 id: 2>,
    #<Editor:0x00007f63a612bae0 id: 3>]

100

为了构建一个匹配缺失内容的查询,我们需要让编辑器没有角色.left_joins就是这样.

>> Editor.left_joins(:roles)
=> [#<Editor:0x00007f639fa962d0 id: 1>,
    #<Editor:0x00007f639fa96618 id: 2>,
    #<Editor:0x00007f639fa96528 id: 3>,
    #<Editor:0x00007f639fa963e8 id: 3>]

# NOTE: we can see the returned database result
#       and select `roles.id` column to get some context.
>> ActiveRecord::Base.connection.execute(Editor.select("editors.*", "roles.id as role_id").left_joins(:roles).to_sql).to_a

=> [{"id"=>1, "role_id"=>nil}, # NOTE: this `role_id` is what we're looking for
    {"id"=>2, "role_id"=>1},
    {"id"=>3, "role_id"=>2},
    {"id"=>3, "role_id"=>3}]

# NOTE: now just find where `roles.id` is `nil`
>> Editor.left_joins(:roles).where(roles: { id: nil})
=> [#<Editor:0x00007f639fc2c068 id: 1>]     

Rails有一个用于此查询的快捷方式:

>> Editor.where.missing(:roles)
=> [#<Editor:0x00007f639fd33358 id: 1>] 

# NOTE: this works too and does a single join
>> Editor.where.missing(:editor_roles)
=> [#<Editor:0x00007f63a59e0dd0 id: 1>]

100

Sql相关问答推荐

如何将varchar传递给tvf并使用该参数来查询结果SQL服务器

获取家谱树中第一次出现的特定信息,然后停止

GROUP BY与多个嵌套查询T—SQL

GROUP BY和GROUP_CONCAT用于计算比赛排名

出现5次后,将所有正斜杠替换为连字符

PostgreSQL-按距离阈值挤压相邻行的性能

从给定数据中查找下一个工作日期

同时插入和更新记录

将 jsonb 数组中的对象取消嵌套到单独的行中

如何在android房间中进行多个加入

group by 并根据同表中其他列的某些条件获取 group by 中的某一列值

使用长 IN 子句的 SQL 优化

每次计数器增加时通过运行总重置进行分组

Postgres如何在一个日历周中前进和回填值

使用给定的变量对在循环中执行更新语句

将 MERGE 语句与 Oracle PL/SQL 表类型一起使用时,导致无效数据类型错误的原因是什么?

基于源表的 SQL INSERT、UPDATE 和 DELETE

如何在 RavenDB Studio (RQL) 中插入更新文档

BigQuery - 将 TIMESTAMP 转换为 HH:MM:SS,然后识别 TIME_DIFF

如何根据 Amazon Athena 中的多个列值删除重复行?