Active Record优化:.includes()生成一个非常慢的DISTINCT查询,我该如何避免它?

时间:2012-07-04 13:32:18

标签: sql ruby-on-rails activerecord query-optimization

我有以下两种关系:

class CfThread < ActiveRecord::Base
  self.primary_key = 'thread_id'
  self.table_name  = 'cforum.threads'

  belongs_to :forum, class_name: 'CfForum', :foreign_key => :forum_id
  has_many :messages, class_name: 'CfMessage', :foreign_key => :thread_id

  attr_accessible :thread_id, :tid, :slug, :forum_id, :archived, :created_at, :updated_at
end

class CfMessage < ActiveRecord::Base
  has_one :owner, class_name: 'CfUser', :foreign_key => :user_id
  has_many :flags, class_name: 'CfFlag'

  belongs_to :thread, class_name: 'CfThread', :foreign_key => :thread_id

  attr_accessible :message_id, :mid, :thread_id, :subject, :content,
    :author, :email, :homepage, :deleted, :user_id, :parent_id,
    :updated_at, :created_at
end

我想查询最后10个线程及其消息,而不会产生n + 1个查询情况:

@threads = CfThread.includes(:messages).order('cforum.threads.created_at DESC').limit(10)

使用DISTINCT生成一个非常慢的查询:

SELECT DISTINCT "cforum"."threads".thread_id, cforum.threads.created_at AS alias_0 FROM "cforum"."threads" LEFT OUTER JOIN "cforum"."messages" ON "cforum"."messages"."thread_id" = "cforum"."threads"."thread_id" ORDER BY cforum.threads.created_at DESC LIMIT 10

查询规划器希望对线程和消息进行全表扫描。

使用纯SQL可以很容易地优化这种情况,但是有没有使用AR的方法呢?我可以在不产生n + 1个查询的情况下避免使用DISTINCT吗?

问候,  CK

编辑:索引如下:

主题:

"threads_pkey" PRIMARY KEY, btree (thread_id)
"index_cforum.threads_on_slug" UNIQUE, btree (slug)
"index_cforum.threads_on_archived" btree (archived)
"index_cforum.threads_on_tid" btree (tid)
"threads_created_at_idx" btree (created_at, updated_at)

消息:

"messages_pkey" PRIMARY KEY, btree (message_id)
"index_cforum.messages_on_mid" btree (mid)
"index_cforum.messages_on_thread_id" btree (thread_id)

1 个答案:

答案 0 :(得分:2)

我认为您的订单会略微混淆活动记录 - 我认为活动记录认为它需要加入的列才能使订单生效。所以它使用了基于连接的include变体,当你想限制它时,它又需要一个独特的查询。

显然,顺序仅取决于CfThread模型,它只是混淆的AR启发式算法。一种方法是强制执行

所使用的急切加载模式
CfThread.preload(:messages).order('cforum.threads.created_at DESC').limit(10)