当查询结果由has_many:through关联排序时,应使用哪些索引

时间:2012-05-22 15:15:35

标签: ruby-on-rails ruby-on-rails-3

问题可能很长但问题实际上很简单。我有3个型号:列表,外观和电影。列表通过外观有很多电影,它们应该按照连接模型的属性等级排序。那么我应该使用哪些索引?这就是我的模型目前的样子:

# Models
class Movie < ActiveRecord::Base  
  has_many :appearances, :dependent => :destroy
  has_many :lists, :through => :appearances
end

class Appearance < ActiveRecord::Base
  belongs_to :list
  belongs_to :movie
end

class List < ActiveRecord::Base
  has_many :appearances, :dependent => :destroy
  has_many :movies, :through => :appearances

  def self.find_complete(id)
    List.includes({:appearances => :movie}).where("appearances.rank IS NOT NULL").order("appearances.rank ASC").find(id)
  end
end

以下是我已有的索引。我需要综合指数吗?或者只是按排名指数?

# Tables
class CreateAppearances < ActiveRecord::Migration
  def change
    create_table :appearances do |t|
      t.integer :list_id, :null => false
      t.integer :movie_id, :null => false
      t.integer :rank
    end
    add_index :appearances, :list_id
    add_index :appearances, :movie_id
  end
end

最后,有没有办法重构find_complete List方法?就像使用default_scope一样?不要忘记等级可能为空。

3 个答案:

答案 0 :(得分:1)

ActiveRecord正在进行两次查询。

SELECT DISTINCT `lists`.id
FROM `lists` LEFT OUTER JOIN `appearances` ON `appearances`.`list_id` = `lists`.`id`
             LEFT OUTER JOIN `movies` ON `movies`.`id` = `appearances`.`movie_id`
WHERE `lists`.`id` = 1 AND (appearances.rank IS NOT NULL)
ORDER BY appearances.rank ASC LIMIT 1

SELECT `lists`.`id` AS t0_r0, `lists`.`name` AS t0_r1, `lists`.`created_at` AS t0_r2, `lists`.`updated_at` AS t0_r3, `appearances`.`id` AS t1_r0, `appearances`.`list_id` AS t1_r1, `appearances`.`movie_id` AS t1_r2, `appearances`.`rank` AS t1_r3, `appearances`.`created_at` AS t1_r4, `appearances`.`updated_at` AS t1_r5, `movies`.`id` AS t2_r0, `movies`.`name` AS t2_r1, `movies`.`created_at` AS t2_r2, `movies`.`updated_at` AS t2_r3
FROM `lists` LEFT OUTER JOIN `appearances` ON `appearances`.`list_id` = `lists`.`id`
             LEFT OUTER JOIN `movies` ON `movies`.`id` = `appearances`.`movie_id`
WHERE `lists`.`id` = 1 AND `lists`.`id` IN (1) AND (appearances.rank IS NOT NULL)
ORDER BY appearances.rank ASC

在这两种情况下,您需要的是一个包含lists.id和appearances.rank的索引。某些数据库将在多个表上创建索引。我也不知道你的数据库是否足够聪明,可以在appearances.list_id和appearances.rank上使用索引,但这值得一试。

add_index :appearances, [:list_id, :rank]

如果这没有帮助,至少索引:排名,所以数据库可能能够避免排序。

答案 1 :(得分:0)

您必须查看Rails将针对您的特定用例输出的查询。

我最好的猜测是在外键列上添加索引然后使用分析器一旦应用程序完成并加载数据,以查看特定查询是否表现不佳并修复它们!

永远不要试图超越你的数据库,只是衡量和修复 - 其他任何东西都可能比你节省更多的性能。

你在这里提出的是一种过早优化的形式,也有点猜测,考虑Rails3 Arel如何推迟执行,以便实际需要什么索引在很大程度上取决于这种关联的使用方式以及如何遍历。

答案 2 :(得分:0)

重新检查您的关联。 如果

  

列表通过外观包含许多电影

class List < ActiveRecord::Base
 has_many :appearances, :dependent => :destroy
 has_many :movies, :through => :appearances
end

class Appearance < ActiveRecord::Base
 belongs_to :list, :foreign_key => list_id
 has_many :movies
end

class Movie < ActiveRecord::Base  
 belongs_to :appearance, :foreign_key => appearance_id, :dependent => :destroy  
end

至于find_complete方法,我不明白为什么把它变成一个范围。由于你已经在List模型中并且看起来非常奇怪,调用List.includes ....里面可能需要做得有点短。本身。

看不到需要那个movie_id索引

class CreateAppearances < ActiveRecord::Migration
  def change
    create_table :appearances do |t|
      t.integer :list_id, :null => false
      t.integer :movie_id, :null => false
      t.integer :rank
    end
  add_index :appearances, :list_id
 end
end

还需要一个movie_id索引作为你的电影表。

我希望我没有遗漏任何东西