has_many的索引:通过

时间:2008-10-21 22:37:12

标签: sql ruby-on-rails ruby database has-many

假设您有两个模型,User和City,由第三个模型CityPermission加入:

class CityPermission < ActiveRecord::Base
  belongs_to :city
  belongs_to :user
end

class City < ActiveRecord::Base
  has_many :city_permissions
  has_many :users, :through => :city_permissions
end

class User < ActiveRecord::Base
  has_many :city_permissions
  has_many :cities, :through => :city_permissions
end

目前,我使用以下迁移代码段创建了连接表和表的索引:

create_table :city_permissions do |t|
      t.integer :user_id, :city_id
      t.other_fields ...
end

add_index(:city_permissions, :user_id)
add_index(:city_permissions, :city_id)

这些是创建的最佳索引吗?这些索引是否允许通过连接表来回快速访问,以及在表格内快速查找,还是有其他更好的方法?为了稍微改写一下,给定cityuser的这些索引是City和User类的实例变量,允许city.userscity.city_permissionsuser.citiesuser.city_permissions表现同样出色?

2 个答案:

答案 0 :(得分:4)

对我来说很好。

生成的连接应该只在实体表的PK ID上,或者在连接表中的FK ID上 - 这两个都是索引。

查看生成的ActiveRecord SQL并将其与索引进行比较可能会很好。

根据您所在的数据库,您可以通过Explain计划运行该SQL(或者存在任何工具,我在想Oracle)

为简化您的代码,您还可以查看使用has_and_belongs_to_many。这样你就可以摆脱CityPermission对象了(除非你想用它来存储数据)

答案 1 :(得分:1)

以下是ActiveRecord为user.cities生成的SQL:

SELECT `cities`.* FROM `cities` INNER JOIN city_permissions ON (cities.id = city_permissions.city_id) WHERE (city_permissions.user_id = 1 )

下面的EXPLAIN结果:

+----+-------------+------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-------------------------------------------------+------+-------------+
| id | select_type | table            | type   | possible_keys                                                       | key                               | key_len | ref                                             | rows | Extra       |
+----+-------------+------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-------------------------------------------------+------+-------------+
|  1 | SIMPLE      | city_permissions | ref    | index_city_permissions_on_user_id,index_city_permissions_on_city_id | index_city_permissions_on_user_id | 5       | const                                           |    1 | Using where |
|  1 | SIMPLE      | cities           | eq_ref | PRIMARY                                                             | PRIMARY                           | 4       | barhopolis_development.city_permissions.city_id |    1 |             |
+----+-------------+------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-------------------------------------------------+------+-------------+

这是ActiveRecord为user.city_permissions生成的SQL:

SELECT * FROM `city_permissions` WHERE (`city_permissions`.user_id = 1)

使用该查询的EXPLAIN结果:

+----+-------------+------------------+------+-----------------------------------+-----------------------------------+---------+-------+------+-------------+
| id | select_type | table            | type | possible_keys                     | key                               | key_len | ref   | rows | Extra       |
+----+-------------+------------------+------+-----------------------------------+-----------------------------------+---------+-------+------+-------------+
|  1 | SIMPLE      | city_permissions | ref  | index_city_permissions_on_user_id | index_city_permissions_on_user_id | 5       | const |    1 | Using where |
+----+-------------+------------------+------+-----------------------------------+-----------------------------------+---------+-------+------+-------------+

看起来确实正常工作。从MySQL手册:

eq_ref

对于前面表格中的每个行组合,从此表中读取一行。除了system和const类型之外,这是最好的连接类型。当连接使用索引的所有部分并且索引是PRIMARY KEY或UNIQUE索引时使用它。

REF

对于前面表格中的每个行组合,将从此表中读取所有具有匹配索引值的行。如果连接仅使用键的最左前缀或者键不是PRIMARY KEY或UNIQUE索引(换句话说,如果连接不能基于键值选择单行),则使用ref。如果使用的密钥只匹配几行,这是一个很好的连接类型。