为什么Foo.first返回最后一条记录?

Chr*_*ini 4 postgresql activerecord ruby-on-rails ruby-on-rails-3

我有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
Run Code Online (Sandbox Code Playgroud)

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

我可以通过以下方式轻松解决此问题:Foo.order('id ASC').first相反.但寻找解释.

mu *_*ort 6

它没有任何逻辑,如果对first(或last那个问题)有任何意义,那么如果你忽略了将显式顺序指定first为当前范围链的参数或作为当前范围链的一部分,它将引发异常.除非指定了明确的顺序,否则在关系数据库的上下文中都first没有last任何意义.

我的猜测是,如果没有明确的话,谁写的first假设order by whatever_the_pk_is是隐含的order by.然后他们可能做了一些实验来经验验证他们的假设,它恰好按照他们所期望的特定表格和数据库进行工作(迷你咆哮:这就是为什么你永远不会假设未指明的行为;如果某个特定的行为是没有明确指定,即使当前实现的行为方式或者经验证据表明它的行为方式,也不要假设它.

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

limit(1).to_a[0]
Run Code Online (Sandbox Code Playgroud)

没有明确的排序,所以你得到数据库感觉使用的随机排序,可能是order by pk或者它可能是磁盘上的表的块顺序.如果你追踪M.last,你会得到find_last:

def find_last
  #...
        reverse_order.limit(1).to_a[0]
  #...
end
Run Code Online (Sandbox Code Playgroud)

而且reverse_order:

def reverse_order
  relation = clone
  relation.reverse_order_value = !relation.reverse_order_value
  relation
end
Run Code Online (Sandbox Code Playgroud)

@reverse_order_value实例变量没有被初始化,因此会一开始就是nil!将它变成一个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?
  #...
Run Code Online (Sandbox Code Playgroud)

还有作者关于订购的无效假设让所有人都知道.该行应该是:

raise 'Specify an order you foolish person!' if order_query.empty?
Run Code Online (Sandbox Code Playgroud)

我建议你总是使用.order(...).limit(1).first而不是first或者last说一切都很好而且明确; 当然,如果你想要改变last这种.order状况.或者你总是可以说.first(:order => :whatever)并且.last(:order => :whatever)再次使一切都明确.