トップ «前の日記(2005-04-03) 最新 次の日記(2005-04-05)» 編集

3 日坊主日記


2005-04-04 [長年日記]

_ [Rails] ActiveRecord: order by は必要悪ってことですか -> allocate が消されていました -> いや違う

 @customer.services.find_all(nil, "service_at desc").each { |service| }

とすると

 SELECT * FROM services WHERE customer_id = 3
 SELECT * FROM services WHERE customer_id = 3 ORDER BY service_at desc

が発行される。これは無駄に見えます。 これは Ruby 側で sort しろということか?

 @customer.services.sort_by { |service| service.service_at }.reverse.each { |service| }

sort はどっちでやるべきなんでしょう。教えて O/R に詳しい人!

でも limit が効かないなら現状はまずいんじゃないか。

あれ? force_reload が効いてないのか。

@customer の @services が proxy じゃなくて [Service, ...] な Array になってる...

environment.rb の ActiveRecord::Base.establish_connection の位置を logger 設定後にしないと test.log に記録されないね。

 p ActiveRecord::Associations::HasManyAssociation.new(customer, "services", "Service", "customer_id", {}).class
 #=> Array

なんでやねーん。

-rdebug で追ってみる。

 C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record/associations/
 has_many_association.rb:142:            @counter_sql << " AND #{interpolate_sql(
 @conditions)}" if @conditions
 (rdb:1)
 n
 C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record/associations/
 association_proxy.rb:23:        load_target
 (rdb:1) l
 [18, 27] in C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record/a
 ssociations/association_proxy.rb
    18          reset
    19          load_target
    20        end
    21
    22        def method_missing(symbol, *args, &block)
 => 23          load_target
    24          @target.send(symbol, *args, &block)
    25        end
    26
    27        def respond_to?(symbol, include_priv = false)
 (rdb:1)

おおっと! テレポーター

探索再開。

 (rdb:1) l
 [137, 146] in C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record
 /associations/has_many_association.rb
    137            elsif @options[:finder_sql]
    138              @options[:counter_sql] = @options[:finder_sql].gsub(/SELECT
 (.*) FROM/i, "SELECT COUNT(*) FROM")
    139              @counter_sql = interpolate_sql(@options[:counter_sql])
    140            else
    141              @counter_sql = "#{@association_class_primary_key_name} = #{@
 owner.quoted_id}"
 => 142              @counter_sql << " AND #{interpolate_sql(@conditions)}" if @c
 onditions
    143            end
    144          end
    145      end
    146    end
 (rdb:1) p @conditions
 nil
 (rdb:1) w
 --> #1 C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record/associ
 ations/has_many_association.rb:142:in `construct_sql'
     #2 C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record/associ
 ations/has_many_association.rb:8:in `initialize'
     #3 ar.rb:15
 (rdb:1)

ここで飛ぶようには見えないのだが...

いや。ちょっと待てよ...

 (rdb:1) l
 [18, 27] in C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record/a
 ssociations/association_proxy.rb
    18          reset
    19          load_target
    20        end
    21
    22        def method_missing(symbol, *args, &block)
 => 23          load_target
    24          @target.send(symbol, *args, &block)
    25        end
    26
    27        def respond_to?(symbol, include_priv = false)
 (rdb:1) p symbol
 :class
 (rdb:1) p args
 []
 (rdb:1) p block
 nil
 (rdb:1)

...

.class を外してみました。

 (rdb:1) l
 [18, 27] in C:/usr/lib/ruby/gems/1.8/gems/activerecord-1.9.1/lib/active_record/a
 ssociations/association_proxy.rb
    18          reset
    19          load_target
    20        end
    21
    22        def method_missing(symbol, *args, &block)
 => 23          load_target
    24          @target.send(symbol, *args, &block)
    25        end
    26
    27        def respond_to?(symbol, include_priv = false)
 (rdb:1) p symbol
 :inspect
 (rdb:1) p args
 []
 (rdb:1) p block
 nil
 (rdb:1)

...

わかった!

  • association_proxy.rb:
 module ActiveRecord
   module Associations
     class AssociationProxy #:nodoc:
       alias_method :proxy_respond_to?, :respond_to?
       instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?|^proxy_respond_to\?|^send)/ }

こいつだ!

       instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?|^proxy_respond_to\?|^send|^allocate|^inspect)/ }

...

いや違う。これは p でデバッグしたために inspect で method_missing となっただけだ。観測すると値が変わって見えるわけだな。

_ [Rails] ActiveRecord: order by は必要悪ってことですか(2)

仕切り直し。よく見ると services.find_all の前に services.count としている。

 @customer.services.count
 @customer.services.find_all(nil, "service_at desc").each { |service| }

とすると

 Service Count  SELECT COUNT(*) FROM services WHERE customer_id = 3
 Service Load  SELECT * FROM services WHERE customer_id = 3
 Service Columns  SHOW FIELDS FROM services
 Service Load  SELECT * FROM services WHERE customer_id = 3 ORDER BY service_at desc

が発行される。

services.count をコメントアウトすると、ログから上3行は消えて

 Service Load  SELECT * FROM services WHERE customer_id = 3 ORDER BY service_at desc

だけになるようだ。

本日のツッコミ(全1件) [ツッコミを入れる]
# babie (2005-04-05 12:57)

SDI/RAD は order by 禁止らしい。(うろ覚え)<br>コネクション数次第かな?

[]