活动记录 - 具有多列的IN
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了活动记录 - 具有多列的IN相关的知识,希望对你有一定的参考价值。
我有一个查询需要从一个完全满足两列要求的表中获取。所以,如果我有users
表与列,age
和score
。
SELECT * FROM users where (age, score) IN ((5,6), (9,12), (22,44)..)
在我的网络应用程序中,我从ajax请求得到这对,并且数量可能非常大。如何为此构建Active Record查询?
我正在研究postgres数据库
理想情况下,我们将根据输入构建一个查询字符串。例如
ages_and_scores = [ [5, 6], [9, 12], [22, 44] ]
query_string = ages_and_scores.map do |pair|
"(age = #{pair[0]} AND score = #{pair[1]})"
end.join(" OR ")
# => (age = 5 AND score = 6) OR (age = 9 AND score = 12) OR (age = 22 AND score = 44)
最后,您的查询将是
User.where(query_string)
您可以更正如何构建查询字符串的逻辑,因为ages_and_scores
与我的示例的格式不同。
起色
ages_and_scores = [ [5, 6], [9, 12], [22, 44] ]
query_params = []
query_template = ages_and_scores.map{ |_| "(age = ? AND score = ?)" }.join(" OR ")
# => (age = ? AND score = ?) OR (age = ? AND score = ?) OR (age = ? AND score = ?)
User.where(query_template, *ages_and_scores.flatten)
另一种方法,用于基于多列IN子句重构OP使用的确切查询。
一种更紧凑的SQL形式,具有可论证的更好的语义:
ages_and_scores = [ [5, 6], [9, 12], [22, 44] ]
query_string = ages_and_scores.map { |pair| "(#{pair[0]},#{pair[1]})" }.join(",")
User.where("(age, score) IN (#{query_string})")
嘿,你可以在mysql中尝试这种方式:
ages_and_scores = [ [5, 6], [9, 12], [22, 44] ]
User.where("CONCAT(age,',', score) in (?)",ages_and_scores.map{|b| "#{b[0]},#{b[1]}"})
在PG数据库中,您可以直接使用Concat:
(age || ' ,' || score)
另一个解决方案是使用Arel,这将有助于保持数据库不可知。
ages_and_scores = [ [5, 6], [9, 12], [22, 44] ]
first_age, first_score = ages_and_scores.shift
t = User.arel_table
users = ages_and_scores.inject(User.where(t[:age].eq(first_age)).where(t[:score].eq(first_score))) { |query, age_score| query.or(User.where(t[:age].eq(age_score[0])).where(t[:score].eq(age_score[1]))) }
基本上这是如何工作的:
- 将第一个值对与数组分开
- 获取arel表以便您可以构建查询
- 使用Ruby的
inject
模块中的Enumerable
方法迭代遍历所有or
条件的数组的其余部分...但是从第一个where
查询中的初始两个值开始。
最后,您将获得一个Arel
查询,该查询返回符合您条件的所有用户...并且它应该适用于Rails(Arel)支持的任何数据库。
在rails 5中,您可以使用OR,这样您就可以:
ages_and_scores = [ [5, 6], [9, 12], [22, 44] ]
ages_and_scores.each do |age, score|
User.where(age: age).where(score: score)
end.reduce(&:or)
# => should produce something like:
# SELECT * FROM users WHERE (`users`.`age` = 5 AND `users`.`score` = 6 OR `users`.`age` = 9 AND `users`.`score` = 12 OR `users`.`age` = 22 AND `users`.`score` = 44)
我相信这是sql-injection免费和纯ActiveRecord。
基于Yuki Inoue的评论。我已经修改了一点,它工作顺利
ages_and_scores = [ [5, 6], [9, 12], [22, 44] ]
ages_and_scores.each do |age, score|
User.where(age: age).map(score: score)
end.reduce(&:or)
# => should produce something like:
# SELECT * FROM users WHERE (`users`.`age` = 5 AND `users`.`score` = 6 OR `users`.`age` = 9 AND `users`.`score` = 12 OR `users`.`age` = 22 AND `users`.`score` = 44)
以上是关于活动记录 - 具有多列的IN的主要内容,如果未能解决你的问题,请参考以下文章
一旦单击带有 in 片段的回收器列表项,如何将片段意向活动,以及如何获取回收器项目值?
ElasticSearch学习问题记录——Invalid shift value in prefixCoded bytes (is encoded value really an INT?)(代码片段