新手问题 where 的一个蛋疼设定

Catherine · 2016年03月31日 · 最后由 42thcoder 回复于 2016年04月01日 · 2871 次阅读

例如我把一个千辛万苦排序出来的 ids 数组,重托给 where 去查询。结果 where 这小婊砸不按 ids 数组里的顺序乖乖查,按 id 的升序去查的。

ids = [ 3,2,1]
A.where(ids).map(&:id)
=> [1,2,3]

诸君有没有遇到类似问题,是如何解决的?

#1 楼 @a4652097 order 只能按字段排序吧,我现在的 id 是自己筛选出来的一个数组。例如 ids=[3,1,2]

这种排序不要让数据库去做,让它做自己喜欢的事啦。

#3 楼 @sandy_xu 我就是不要它排,所以 ids 都是排序好了的 ids 数组,只希望 where 按个查,按下标老老实实查出来我就可以用了。。可 where 出来 map(&:id) 发现数据的 ids 被惨无人道的从小到大玩弄了一次...

ids.map{|id| A.find_by(XX: id).pluck(:id)} 这样可行么?

6 楼 已删除

#5 楼 @killernova 这样巨慢,N+1 Queries

你没考虑到 id 不存在的时候么

#8 楼 @ywjno 过滤数组 ids,筛选的主体是 A。where 的主体也是 A,所以筛选出来的 ids 肯定是主体 A 的一个子集。如果筛选出来的 id 不存在,因为是数组,最多是个 nil 吧,compact 一下就可以了啊

其实我想说的是,多写两行代码可以解决问题的

ids = [3, 2, 1]
hash = {}
ids.each {|id| hash[id] = nil}
A.where(ids).each {|a| hash[a.id] = a}
hash.values # 这里就是按照 ids 排序的 A,id 不存在的话是 nil

我遇到的一个问题跟你的类似:

user_names = ['steve', 'bob', 'cook']
User.where(name: user_names).sort_by { |user| user_names.index(user.name) }

#14 楼 @gonglexin 用 7 楼和 13 楼里提到的方法可以解决,sort_by 后会变成数组,因为后面还有分页需要调用,还需要最后是 relation 对象

同样遇到这个问题,数据库是 poatgresql 目前采用跟#14 楼一样的方法,更蛋疼的是还要考虑 id 找不到的情况,查询结果要对齐。

enable_extension "intarray", 然后使用 idx

这不是 where 的锅~ MySQL 下可以用 filed 来解决

module Extensions::ActiveRecord::FindByOrderedIds
  extend ActiveSupport::Concern
  module ClassMethods
    def find_ordered(ids, table_name)
      return where(id: ids) unless ids.present?
      sanitized_id_string = ids.map {|id| connection.quote(id)}.join(',')
      where(id: ids).order("FIELD(#{table_name}.id, #{sanitized_id_string})")
    end
  end
end

ActiveRecord::Base.include(Extensions::ActiveRecord::FindByOrderedIds)

A.find_ordered([3, 1,2,5]) 就会按顺序查出来了。

A.where(id: ids).order("FIELD(id, #{ids.join(',')})")

#20 楼 @42thcoder 我找到的也是这段代码,忘了在哪里找到的了? 另外,不明白的是为啥要去 connection.quote 一下 id?直接操作传进来的 id 不行么

需要 登录 后方可回复, 如果你还没有账号请 注册新账号