7824


`forecast`カラムを持つ`TerminalValue`というモデルから、

[code]

    pry(main)> c.terminal_values.where(forecast: false).count
       (0.9ms)  SELECT COUNT(*) FROM "terminal_values"  WHERE "terminal_values"."company_id" = $1 AND "terminal_values"."forecast" = 'f'  [["company_id", 2249]]
    => 5
    
[/code]

と正常に取得でき、

[code]

    pry(main)> c.terminal_values.where(forecast: false).last
    PG::SyntaxError: ERROR:  zero-length delimited identifier at or near """"
    LINE 1: ...ues"."forecast" = 'f'  ORDER BY "terminal_values"."" DESC LI...
                                                                 ^
    : SELECT  "terminal_values".* FROM "terminal_values"  WHERE "terminal_values"."company_id" = $1 AND "terminal_values"."forecast" = 'f'  ORDER BY "terminal_values"."" DESC LIMIT 1
    ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:  zero-length delimited identifier at or near """"
    LINE 1: ...ues"."forecast" = 'f'  ORDER BY "terminal_values"."" DESC LI...
                                                                 ^
    : SELECT  "terminal_values".* FROM "terminal_values"  WHERE "terminal_values"."company_id" = $1 AND "terminal_values"."forecast" = 'f'  ORDER BY "terminal_values"."" DESC LIMIT 1
    from /Users/ironsand/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/activerecord-4.1.0/lib/active_record/connection_adapters/postgresql_adapter.rb:862:in `prepare'
    
[/code]

と、`last`を使い最後に取得したものを取ろうとするとエラーになってしまいます。

ところが`[-1]`を使えば正常に取得出来ました。

[code]

    pry(main)> c.terminal_values.where(forecast: false)[-1]
      TerminalValue Load (0.8ms)  SELECT "terminal_values".* FROM "terminal_values"  WHERE "terminal_values"."company_id" = $1 AND "terminal_values"."forecast" = 'f'  [["company_id", 2249]]
    
[/code]

エラー原因は`"terminal_values".""`というSQL文として構文エラーになるものをRailsが吐き出すためとはわかりました。

また本質的な問題部分はそこではなく`c.terminal_values.where(forecast:
false)`に`order(:date)`を付随させ順序を明確にする必要が有ることもわかったのですが、何故`last`と`[-1]`で違う動作になるような実装になっているかの疑問が残りました。

この違いは何か意図があっての動作なのでしょうか？


CC BY-SA 3.0
2015-03-12T05:09:53.807
7820
2015-03-12T06:45:30.773
3271
question
ruby ruby-on-rails
ActiveRecord_AssociationRelation クラスの last メソッドと [-1] の扱いの違いについて


`#last`でエラーになるのは再現しなかったのでそこはよくわからないのですが、`#last`と[-1]ではやっていることが違います。

`#last`ではSQLレベルで「最後のレコード」を取得します。`ORDER BY ... LIMIT 1`に注目してください。

[code]

    irb(main):004:0> Article.where(title: 'hoge').last
      Article Load (0.4ms)  SELECT  "articles".* FROM "articles" WHERE "articles"."title" = ?  ORDER BY "articles"."id" DESC LIMIT 1  [["title", "hoge"]]
    
[/code]

`[-1]`はDBからは検索条件に合致したレコードを全件取得し、スクリプトの中でその配列の最後の要素を取り出しています。。`#last`と違い`ORDER
BY ... LIMIT 1`がありません。

[code]

    irb(main):005:0> Article.where(title: 'hoge')[-1]
      Article Load (0.4ms)  SELECT "articles".* FROM "articles" WHERE "articles"."title" = ?  [["title", "hoge"]]
    
[/code]

`#[]`をSQL組み立てのAPIにするのはできないことはないでしょうが、`.last(1)`と`[-1]`の対応は簡単ですが、`.last(5)`とか`#first`も考え始めると面倒そうですし、

[code]

    records = Article.where(...)
    records[0]
    records[1]
       :
    
[/code]

みたいなことをするとクエリが複数回発生することになります。直感的とは言いがたいのではないでしょうか。


CC BY-SA 3.0
2015-03-12T06:45:30.773
7824
2015-03-12T06:45:30.773
5793
7820
answer