4

FooID が 1 と 2 の2 つのレコードが にあります。両方ともこの順序で作成されました。Postgres では、レコードに固有の順序がないことに注意してください。

Rails コンソールで。最後のレコードFoo.firstを返します。最初のレコードを返すというFoo.last印象を受けました。Foo.first

これがキャッチです。SQL クエリは次のようになります。

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

2 番目のクエリ ( Foo.last) にはORDER BY DESC. では、なぜ AR にはORDER BY ASCforがないの.firstでしょうか? この背後にあるロジックは何ですか? 少し「矛盾」しているようです。

これは、代わりに次のようにすることで簡単に解決できますFoo.order('id ASC').first。しかし、説明を探しています。

4

2 に答える 2

6

何らかの意味がある場合first(またはそれについて)、現在のスコープチェーンlastへの引数またはその一部として明示的な順序を指定することを怠った場合、例外が発生します。明示的な順序付けが指定されていない限り、リレーショナル データベースのコンテキストでは意味がありませfirstfirstlast

私の推測ではfirstorder 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インスタンス変数は初期化されていないため、 として開始され、nila!は に変わり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代わりにfirstorを常に使用することをお勧めします。lastもちろん、必要に応じlastて条件を逆にし.orderます。.first(:order => :whatever)または、いつでもand と言って、.last(:order => :whatever)すべてを明示的にすることもできます。

于 2012-05-01T01:23:00.790 に答える