使用Arel创建WHERE(列)IN(值)子句?

问题描述 投票:2回答:3

是否有办法在Arel中以编程方式创建where子句,其中列和值是分别指定的?

SELECT users.*
WHERE (country, occupation) IN (('dk', 'nurse'), ('ch', 'doctor'), ...

说输入是我们要匹配的非常长的对列表。而且我们不想创建一个长的WHERE (a = b AND b = c) OR ...子句,而这正是使用.where(country: 'dk', occupation: 'nurse').or(where(country: 'ch', occupation: 'doctor'))获得的子句。

到目前为止,我仅具有基本的字符串操作:

columns = [:country, :occupation]
pairs = [['dk', 'nurse'], ['ch', 'doctor']]
User.where(
  "(#{columns.join(', ')}) IN (#{ pairs.map { '(?, ?)' }.join(', ')})", 
  *pairs
)

不仅与查询WHERE (columns) IN (values)的长度有关,还与perform much better on Postgres(以及其他查询有关),因为它可以使用仅索引扫描,而OR会引起位图扫描。

我不是在问如何生成WHERE AND OR子句,使用ActiveRecord确实很简单:

@users = array_of_tuples.inject(nil) do |scope, (country, occupation)|
  if scope
    scope.or(where(id: country, occupation: occupation))
  else
    where(id: country, occupation: occupation) # this handles the initial iteration
  end
end
ruby-on-rails arel
3个回答
0
投票

这仍然会生成较长的查询,并且查询之间会出现'OR'。但是我觉得这是实现您想要的目标的一种优雅/不同的方法。

ut = User.arel_table
columns = [:country, :occupation]
pairs = [['dk', 'nurse'], ['ch', 'doctor']]

where_condition = pairs.map do |pair|
  "(#{ut[columns[0]].eq(pair[0]).and(ut[columns[1]].eq(pair[1])).to_sql})"
end.join(' OR ')

User.where(where_condition)

-1
投票

我认为我们可以使用[here]进行Array Conditions来做到这一点>

# notice the lack of an array as the last argument
Model.where("attribute = ? OR attribute2 = ?", value, value)

此外,正如提到的here,我们可以使用SQL in语句:

Model.where('id IN (?)', [array of values])

或者简单地,如kdeisz所指出的(使用Arel创建SQL查询):

Model.where(id: [array of values])

我还没有尝试过,但是您可以尝试使用这些示例进行探索。

总是很乐意提供帮助!


-1
投票

我最终尝试了这种不同的方法。希望它对您有用。

© www.soinside.com 2019 - 2024. All rights reserved.