4

我有 2 条记录Foo,id 分别为 1 和 2。两者都是按此顺序创建的。请记住,在 Postgres 中,记录没有固有的顺序。

在 Rails 控制台中。Foo.firstFoo.last返回最后一条记录。我的印象是Foo.first会返回第一条记录。

这就是问题所在。SQL 查询如下所示:

SELECT "foos".* FROM "foos" LIMIT 1
SELECT "foos".* FROM "foos" ORDER BY "foos"."id" DESC LIMIT 1

第二个查询 ( Foo.last) 有一个ORDER BY DESC. 那么为什么 AR 没有ORDER BY ASCfor.first呢?这背后的逻辑是什么?似乎有点“不一致”。

我可以通过以下方式轻松解决这个问题:Foo.order('id ASC').first相反。而是寻求解释。

4

2 回答 2

6

它没有任何逻辑,如果有任何意义first(或就此而言),那么如果您忽略将显式顺序指定为当前范围链的参数或作为当前范围链的一部分,last它将引发异常。first除非明确指定了顺序,否则在关系数据库的上下文中既不first也没有last任何意义。

我的猜测是,first如果order by whatever_the_pk_is没有明确order by的 . 然后他们可能做了一些实验来凭经验验证他们的假设,并且它恰好与他们检查的特定表和数据库一样工作(迷你咆哮:这就是为什么你永远不会假设未指定的行为;如果一个特定的行为是'没有明确指定,即使当前实现的行为方式是这样,或者如果经验证据表明它的行为方式是那样,也不要假设它)。

如果你追踪一个简单的M.first,你会发现它是这样做的

limit(1).to_a[0]

没有显式排序,因此您可以获得数据库感觉使用的任何随机排序,这可能是order by pk或者它可能是磁盘上表的块顺序。如果你追溯M.last,你会得到find_last

def find_last
  #...
        reverse_order.limit(1).to_a[0]
  #...
end

并且reverse_order

def reverse_order
  relation = clone
  relation.reverse_order_value = !relation.reverse_order_value
  relation
end

实例变量未初始化,@reverse_order_value因此它将以 as 开始,nil而 a!会将其转换为true. 如果你四处寻找如何@reverse_order_value使用,你会得到reverse_sql_order

def reverse_sql_order(order_query)
  order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
  #...

并且有作者关于订购的无效假设暴露给所有人看。那条线应该是:

raise 'Specify an order you foolish person!' if order_query.empty?

我建议您始终使用.order(...).limit(1).first而不是firstorlast以便一切都很好且明确;当然,如果您last愿意,可以颠倒.order条件。或者你总是可以说.first(:order => :whatever).last(:order => :whatever)再次明确一切。

于 2012-05-01T01:23:00.790 回答
2

对于 Rails 版本 4+,如果您不定义任何顺序,它将按主键排序。

# Find the first record (or first N records if a parameter is supplied).
# If no order is defined it will order by primary key.
#
#   Person.first # returns the first object fetched by SELECT * FROM people
#   Person.where(["user_name = ?", user_name]).first
#   Person.where(["user_name = :u", { u: user_name }]).first
#   Person.order("created_on DESC").offset(5).first
#   Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
def first(limit = nil)
  if limit
    if order_values.empty? && primary_key
      order(arel_table[primary_key].asc).limit(limit).to_a
    else
      limit(limit).to_a
    end
  else
    find_first
  end
end

来源:https ://github.com/rails/rails/blob/4-0-stable/activerecord/lib/active_record/relation/finder_methods.rb#L75-L82

于 2016-02-11T12:19:11.470 回答