Индексирование нескольких столбцов в Ruby on Rails

97

Я реализую функцию отслеживания статей, прочитанных пользователем.

  create_table "article", :force => true do |t|
    t.string   "title"
    t.text     "content"
  end

Это моя миграция:

create_table :user_views do |t|
  t.integer :user_id
  t.integer :article_id
end

Таблица user_views всегда будет запрашивать оба столбца, а не только один. Мой вопрос в том, как должен выглядеть мой индекс. Есть ли разница в порядке этих таблиц, должны ли быть какие-то дополнительные опции или что-то еще? Моя целевая БД - Postgres.

add_index(:user_views, [:article_id, :user_id])

Спасибо.

ОБНОВЛЕНИЕ:
поскольку может существовать только одна строка, содержащая одинаковые значения в обоих столбцах (поскольку, зная, читал ли user_id article_id), следует ли мне рассмотреть вариант: unique? Если я не ошибаюсь, это означает, что мне не нужно делать никаких проверок самостоятельно и просто делать вставку каждый раз, когда пользователь посещает статью.

Эмиль Альбек
источник
«Таблица user_views всегда будет запрашивать оба столбца, а не только один». - никогда не будет запроса «найти все статьи, которые просмотрел этот пользователь» или «найти всех пользователей, которые просмотрели эту статью»? Я нахожу это удивительным.
Дэвид Олдридж,

Ответы:

216

Порядок имеет значение при индексации.

  1. Поместите на первое место наиболее избирательное поле, то есть поле, которое сокращает количество строк быстрее всего.
  2. Индекс будет использоваться только постольку, поскольку вы используете его столбцы последовательно, начиная с начала . т.е. если вы индексируете [:user_id, :article_id], вы можете выполнить быстрый запрос на user_idили user_id AND article_id, но НЕ на article_id.

Ваша add_indexлиния миграции должна выглядеть примерно так:

add_index :user_views, [:user_id, :article_id]

Вопрос относительно «уникального» варианта

Самый простой способ сделать это в Rails - использовать validatesв вашей модели со следующей областью видимости uniqueness( документация ):

validates :user, uniqueness: { scope: :article }
сскиррус
источник
7
Порядок имеет огромное значение при индексации. Поместите предложения where слева и заполните индекс столбцами упорядочивания справа. stackoverflow.com/questions/6098616/dos-and-donts-for-indexes
Дени де Бернарди
1
Обратите внимание, что validates_uniqueness_of(и его двоюродный брат validates uniqueness:) склонны к условиям гонки
Бен Обин
1
Как упоминалось в комментариях выше, а также в stackoverflow.com/a/1449466/5157706 и stackoverflow.com/a/22816105/5157706 , подумайте о добавлении уникального индекса в базу данных.
Акаш Агарвал
25

Просто предупреждение о проверке уникальности во время проверки по сравнению с индексом: последняя выполняется базой данных, а праймер - моделью. Поскольку может быть несколько одновременных экземпляров модели, работающих одновременно, проверка зависит от условий гонки, что означает, что в некоторых случаях может не обнаружиться дубликатов (например, отправить дважды одну и ту же форму в одно и то же время).

оливье
источник
Так что лучше? Сторона базы данных или validates_uniqueness_of?
WM
9
Обе. validates_uniqueness_of можно использовать для изящного отображения сообщения об ошибке в приложении, например, при сохранении формы. Ограничение базы данных гарантирует, что вы не получите дублирующих записей, даже если вы знаете, что в модели была указана проверка. Кроме того, вы можете спасти исключение ActiveRecord, а также показать пользователю приятное сообщение.
Угис Озолс
5
@WM Если вам нужно выбрать один, используйте ограничение базы данных. Это будет работать, даже если с вашими данными будут взаимодействовать разные приложения, не относящиеся к RoR, и обеспечивает согласованность в долгосрочной перспективе.
пришвартовался