如何创建(我认为)需要 PostgreSQL 子查询的 Rails 查询?

How to create Rails query that (I think) requires PostgreSQL subquery?

不知道该怎么做,所以标题可能不正确。

每个 User 都有一个字符串类型的字段 country

给定一个 user_id 数组,国家/地区元组 用于查询 ,找到所有匹配的记录。每个用户都必须找到自己的国家。

例如,这里是元组数组。

[1, 'us'],
[2, 'mexico'],
[3, 'us']

这将 return User 1 如果它存在并且它的国家是 'us'.
它也应该 return User 2 如果它存在并且它的国家是 'mexico'.

查询应该return所有匹配结果。

Rails 4.2

我知道这会在纯 SQL 中起作用:例如

SELECT * FROM user
WHERE (id, country) IN ((1, 'us'), (2, 'mexico'), (3, 'us'))

现在我不知道 Rails 如果它是一个成对列表(每个包含两个元素的列表)将如何处理绑定参数。也许这会奏效。

您可以构造原始 sql 并使用活动记录。像这样:

def self.for_multiple_lp(arr=[])
  # Handle case when arr is not in the expected format.
  condition = arr.collect{|a| "(user_id = #{a[0]} AND country = #{a[1]})"}.join(" OR ")
  where(condition)
end

编辑:改进的解决方案

def self.for_multiple_lp(arr=[])
  # Handle case when arr is not in the expected format.
  condition = arr.collect{|a| "(user_id = ? AND country = ?)"}.join(" OR ")
  where(condition, *(arr.flatten))
end

这应该有效。

class User < ApplicationRecord
  def self.query_from_tuples(array_of_tuples)
    array_of_tuples.inject(nil) do |scope, (id, country)|
      if scope
        scope.or(where(id: id, country: country))
      else
        where(id: id, country: country) # this handles the initial iteration
      end
    end
  end
end

结果查询是:

SELECT "users".* FROM "users" 
WHERE (("users"."id" =  AND "users"."country" =  OR "users"."id" =  AND "users"."country" = ) OR "users"."id" =  AND "users"."country" = ) 
LIMIT 

您还可以通过以下方式调整 kamakazis WHERE (columns) IN (values) 查询:

class User < ApplicationRecord
  def self.query_from_tuples_2(array_of_tuples)
    # just a string of (?,?) SQL placeholders for the tuple values
    placeholders = Array.new(array_of_tuples.length, '(?,?)').join(',')
    # * is the splat operator and turns the tuples (flattened) into
    # a list of arguments used to fill the placeholders
    self.where("(id, country) IN (#{placeholders})", *array_of_tuples.flatten)
  end
end

这导致以下查询不那么冗长:

SELECT "users".* FROM "users" 
WHERE ((id, country) IN ((1,'us'),(2,'mexico'),(3,'us'))) LIMIT 

如果您在 [id, country] 上有复合索引,性能也会更好。