$ ruby -v
ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-linux]
$ rails -v
Rails 4.2.0
$ grep DESCR /etc/lsb-release
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
我试图在我的控制器中构建和公开记录子集,如下所示:
@assigned_roles = @user.user_roles.select{|x| x.is_required }.map{ |ur| ur.role }
if @assigned_roles.blank?
@optional_roles = Role.all
else
@optional_roles = Role.where('id NOT IN (?)',@assigned_roles.map{ |r| r.id})
end
它有效,但我正在寻找更优雅的东西。 1}}为空的时候,NOT IN不能正常工作。
我有角色和用户的模型,@assigned_roles
彼此通过have many
带有user_roles
属性。
对不起我总共n00b所以我不知道还有什么要包括在内。非常感谢!
答案 0 :(得分:0)
您的问题是ActiveRecord不知道字符串'id NOT IN (?)'
的含义,因此它替换?
占位符的方法很简单。 AR没有解析SQL片段以查看它应该如何处理占位符,它只知道它应该替换为空数组的占位符,因此它会产生看起来很傻的SQL:
where id not in (null)
在Issue when retrieving records with empty array中对此进行了更多讨论。
但是,如果你让ActiveRecord生成SQL,那么它可以做一些合理的事情,因为它理解它正在做什么而不是盲目地甩掉字符串。这曾经是一个丑陋的NOT IN,但现在很容易,因为你可以说.where.not(...)
:
Role.where.not(:id => @assigned_roles.map(&:id))
如果有@assigned_roles
:
where id not in (6, 11, 23, 42)
如果没有任何@assigned_roles
,等等
where 1 = 1
1 = 1
只是AR建立一个始终为真的WHERE子句的方式(即它等同于Role.all
)。
此外,如果您可以安排@assigned_roles
成为关系(即@assigned_roles = SomeModel.where(...).where(...)
),那么您可以通过以下方式让ActiveRecord使用子查询:
Role.where.not(:id => @assigned_roles.select(:role_id))
将导致SQL如下:
where id not in (
select role_id
from some_models
where ...
)
将查询逻辑保留在数据库中往往比在数据库和Ruby代码之间进行大量聊天更快。