0

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

# 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 一样?不要忘记排名可能为空。

4

3 回答 3

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 的索引。一些数据库会在多个表上创建索引。我也不知道您的数据库是否足够聪明,可以在外观.list_id 和外观.rank 上使用索引,但值得一试。

add_index :appearances, [:list_id, :rank]

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

于 2012-05-22T16:18:36.110 回答
0

您必须查看 Rails 将为您的特定用例输出哪些查询。

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

永远不要尝试超越您的数据库,只需测量和修复 - 其他任何事情很可能会花费您更多的性能而不是节省的性能。

考虑到 Rails3 Arel 将如何延迟执行以便实际需要哪些索引在很大程度上取决于如何使用该关联以及如何遍历它,您在这里提出的问题是过早优化和一些猜测。

于 2012-05-22T15:29:25.177 回答
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

并且还需要为您的电影表创建一个外观 ID 索引。

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

于 2012-05-22T15:42:13.740 回答