Indeksuj w wielu kolumnach w Ruby on Rails

97

Wdrażam funkcję śledzenia, które artykuły użytkownik przeczytał.

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

To jest moja dotychczasowa migracja:

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

Tabela user_views będzie zawsze przeszukiwana w celu wyszukania obu kolumn, a nie tylko jednej. Moje pytanie brzmi, jak powinien wyglądać mój indeks. Czy istnieje różnica w kolejności tych tabel, czy powinno być więcej opcji, czy cokolwiek innego. Moja docelowa baza danych to Postgres.

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

Dzięki.

AKTUALIZACJA:
Ponieważ może istnieć tylko jeden wiersz zawierający te same wartości w obu kolumnach (ponieważ wiedząc, czy identyfikator_użytkownika przeczytał article_id), czy powinienem rozważyć opcję: unique? Jeśli się nie mylę, oznacza to, że nie muszę samodzielnie sprawdzać i po prostu wstawiać wstawkę za każdym razem, gdy użytkownik odwiedza artykuł.

Emil Ahlbäck
źródło
„Tabela user_views będzie zawsze przeszukiwana w celu wyszukania obu kolumn, nigdy tylko jednej”. - nigdy nie pojawi się zapytanie „znajdź wszystkie artykuły, które przeglądał ten użytkownik” lub „znajdź wszystkich użytkowników, którzy przeglądali ten artykuł”? Uważam to za zaskakujące.
David Aldridge

Odpowiedzi:

216

Kolejność ma znaczenie w indeksowaniu.

  1. Najpierw umieść najbardziej selektywne pole, tj. Pole, które najszybciej zawęża liczbę wierszy.
  2. Indeks będzie używany tylko wtedy, gdy używasz jego kolumn w kolejności zaczynającej się od początku . tzn. jeśli indeksujesz na [:user_id, :article_id], możesz wykonać szybkie zapytanie na user_idlub user_id AND article_id, ale NIE na article_id.

Twoja add_indexlinia migracji powinna wyglądać mniej więcej tak:

add_index :user_views, [:user_id, :article_id]

Pytanie dotyczące opcji „unikalnej”

Prostym sposobem na zrobienie tego w Railsach jest użycie validatesw swoim modelu z uniquenessnastępującym zakresem ( dokumentacja ):

validates :user, uniqueness: { scope: :article }
sscirrus
źródło
7
Porządek ma ogromne znaczenie w indeksowaniu. Umieść klauzule where po lewej stronie i uzupełnij indeks kolumnami porządkowania po prawej stronie. stackoverflow.com/questions/6098616/dos-and-donts-for-indexes
Denis de Bernardy
1
Zauważ, że validates_uniqueness_of(i jego kuzyn validates uniqueness:) są podatni na warunki wyścigu
Ben Aubin
1
Jak wspomniano w komentarzach powyżej oraz stackoverflow.com/a/1449466/5157706 i stackoverflow.com/a/22816105/5157706 , rozważ dodanie unikalnego indeksu do bazy danych.
Akash Agarwal
25

Tylko ostrzeżenie o sprawdzaniu unikalności w czasie walidacji w porównaniu z indeksem: to drugie jest wykonywane przez bazę danych, podczas gdy elementarz jest wykonywany przez model. Ponieważ może istnieć kilka jednoczesnych wystąpień modelu działającego w tym samym czasie, walidacja podlega warunkom wyścigu, co oznacza, że ​​w niektórych przypadkach może nie wykryć duplikatów (np. Przesłać dwukrotnie ten sam formularz w tym samym czasie).

olivier
źródło
Więc który jest lepszy? Strona bazy danych lub validates_uniqueness_of?
WM
9
Obie. validates_uniqueness_of może służyć do prawidłowego wyświetlania komunikatu o błędzie w aplikacji, na przykład, gdy formularz zostanie zapisany. Ograniczenie bazy danych sprawi, że nie otrzymasz rekordów dup, nawet wiedząc, że w modelu określono walidację. Dodatkowo możesz uratować wyjątek ActiveRecord, a także pokazać użytkownikowi przyjemną wiadomość.
Uģis Ozols
5
@WM Jeśli musisz wybrać jedną, idź z ograniczeniem bazy danych. Będzie to działać, nawet jeśli różne aplikacje inne niż RoR będą współdziałać z Twoimi danymi i zapewni długoterminową spójność.
zacumuje