是否有办法在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
这仍然会生成较长的查询,并且查询之间会出现'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)
我认为我们可以使用[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])
我还没有尝试过,但是您可以尝试使用这些示例进行探索。
总是很乐意提供帮助!
我最终尝试了这种不同的方法。希望它对您有用。