Индексы для has_many: через - PullRequest
       11

Индексы для has_many: через

7 голосов
/ 22 октября 2008

Предположим, у вас есть две модели, Пользователь и Город, к которым присоединяется третья модель 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)

Это оптимальные индексы для создания? Позволят ли эти индексы быстрый доступ туда-сюда через таблицу соединений, а также быстрый поиск в самой таблице или есть какой-то другой лучший способ? Если перефразировать это немного иначе, будут ли эти индексы, учитывая city и user, переменными экземпляра класса City и User, позволят city.users, city.city_permissions, user.cities и user.city_permissions всем работать одинаково хорошо

Ответы [ 2 ]

4 голосов
/ 22 октября 2008

Хорошо выглядит для меня.

Созданные объединения должны быть либо в идентификаторах PK таблиц сущностей, либо в идентификаторах FK в таблице соединений, которые оба являются индексами.

Вероятно, было бы хорошо взглянуть на сгенерированный ActiveRecord SQL и сравнить его с индексами.

В зависимости от того, в какой базе данных вы находитесь, вы можете запустить этот SQL через план объяснения (или любой другой инструмент, я думаю, Oracle здесь)

Чтобы упростить ваш код, вы также можете использовать has_and_belongs_to_many. Это позволит вам избавиться от объекта CityPermission (если вы не хотите использовать его для хранения данных в себе)

1 голос
/ 22 октября 2008

Вот SQL, который ActiveRecord генерирует для user.cities:

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

ПОЯСНИТЕ результаты ниже:

+----+-------------+------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-------------------------------------------------+------+-------------+
| 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 |             |
+----+-------------+------------------+--------+---------------------------------------------------------------------+-----------------------------------+---------+-------------------------------------------------+------+-------------+

А вот SQL, который ActiveRecord генерирует для user.city_permissions:

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

Из этой таблицы читается одна строка для каждой комбинации строк из предыдущих таблиц. Кроме системного и константного типов, это наилучший возможный тип соединения. Он используется, когда все части индекса используются соединением, а индекс является индексом PRIMARY KEY или UNIQUE.

ссылка

Все строки с соответствующими значениями индекса считываются из этой таблицы для каждой комбинации строк из предыдущих таблиц. ref используется, если соединение использует только крайний левый префикс ключа или если ключ не является индексом PRIMARY KEY или UNIQUE (другими словами, если соединение не может выбрать одну строку на основе значения ключа). Если используемый ключ соответствует только нескольким строкам, это хороший тип соединения.

...