Rails 表串来串去

linjunhalida · 2014年09月05日 · 最后由 linjunhalida 回复于 2014年09月10日 · 2422 次阅读

一个结构:

class User < ActiveRecord::Base
  has_many :members
end

class Member < ActiveRecord::Base
  belongs_to :user
  has_many :member_roles
end

class MemberRole < ActiveRecord::Base
  belongs_to :member
  belongs_to :role
end

class Role < ActiveRecord::Base
end

我要根据一个 role,找到所有 user。可以用:

uids = MemberRole.where(role_id: role.id).joins({member: :user}).pluck('users.id')
User.where(id: uids)

User.where("id in (select user_id from members where id in (select member_id from member_roles where role_id=#{role.id}))")

前一个方法一堆中间值,后一个方法丑陋的 SQL,这种查询很常见,请问大家有什么好的方法?好像可以用squeel弄好看一些?

恩,还有一个方法:

User.joins({members: :member_roles}).where("member_roles.role_id" => role.id)
User.joins(members: :member_roles).where(role_id: role.id)

这个错的。

Update:

User.joins(members: :member_roles).where('member_roles.role_id' => role.id)

@Rei 后面的 where 应该不对,生成的 SQL 会是类似 WHERE "users"."role_id" = 1 这种。

稍微复杂点的 SQL 查询都建议用 Squeel,容易看懂又好维护。

# 假设你有一个 role object
User.joins { members.member_roles }.where { members.member_roles.role == role }

好处:不用拼 SQL,也不用操心任何关联的 key,Squeel 会帮你从 relationship 中去找。

#1 楼 @Rei 这样行不

User.joins(members: :member_roles).where(member_roles: { role_id: role.id })

#3 楼 @Victor 我试了 JOIN 两个表可以这样,三个表我这没有实例测试不了。

@linjunhalida 经测试,我觉得下面的写法是可以的

class User < ActiveRecord::Base
  has_many :members
  has_many :roles, through: :members

  scope :by_role, ->(role) { joins(:roles).where(roles: { id: role }) }
end


class Member < ActiveRecord::Base
  belongs_to :user
  has_many :member_roles
  has_many :roles, through: :member_roles
end

class MemberRole < ActiveRecord::Base
  belongs_to :role
  belongs_to :member
end

class Role < ActiveRecord::Base
end

查询可以这样

User.by_role(2).to_sql

生成的查询是

"SELECT \"users\".* FROM \"users\" INNER JOIN \"members\" ON \"members\".\"user_id\" = \"users\".\"id\" INNER JOIN \"member_roles\" ON \"member_roles\".\"member_id\" = \"members\".\"id\" INNER JOIN \"roles\" ON \"roles\".\"id\" = \"member_roles\".\"role_id\" WHERE \"roles\".\"id\" = 2"

用了 40 分钟,专门建了个应用写了测试......至于查询效率问题,你看看这个 SQL 就知道了......

补充一下:按照楼主的写法,可以改成

class User < ActiveRecord::Base
  has_many :members

  scope :by_role, ->(role) { joins(members: :roles).where(roles: { id: role }) }
end

#3 楼 @Victor 奥,应该可以。

#5 楼 @Victor User 和 Role 用了 4 个模型我就觉得挺奇怪的。

#7 楼 @Rei 按照楼主的设计,那个 MemberRole 只是一个中间表,至于为啥 User 和 Member 还区分开而不用单表继承,我就不知道了。可能这里的 Member 应该是 MemberShip 的意思吧

@Victor 这个 member 表示 user 是属于另外一个对象的(比如 team,organization),所以用 member 表来记录这个链接关系。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号