Index für mehrere Spalten in Ruby on Rails

97

Ich implementiere Funktionen, um zu verfolgen, welche Artikel ein Benutzer gelesen hat.

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

Dies ist meine bisherige Migration:

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

Die Tabelle user_views wird immer abgefragt, um nach beiden Spalten zu suchen, niemals nur nach einer. Meine Frage ist, wie mein Index aussehen soll. Gibt es einen Unterschied in der Reihenfolge dieser Tabellen, sollte es weitere Optionen geben oder was auch immer. Meine Ziel-DB ist Postgres.

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

Vielen Dank.

UPDATE:
Da nur eine Zeile mit denselben Werten in beiden Spalten vorhanden sein kann (da ich weiß, ob user_id article_id gelesen hat), sollte ich die Option: unique berücksichtigen? Wenn ich mich nicht irre, bedeutet dies, dass ich nicht jedes Mal selbst nachsehen und einfach jedes Mal eine Einfügung vornehmen muss, wenn ein Benutzer einen Artikel besucht.

Emil Ahlbäck
quelle
"Die Tabelle user_views wird immer abgefragt, um nach beiden Spalten zu suchen, niemals nur nach einer." - Es wird niemals die Abfrage "Alle Artikel finden, die dieser Benutzer angesehen hat" oder "Alle Benutzer finden, die diesen Artikel angesehen haben" angezeigt. Das finde ich überraschend.
David Aldridge

Antworten:

212

Die Reihenfolge spielt bei der Indizierung eine Rolle.

  1. Stellen Sie das selektivste Feld an die erste Stelle, dh das Feld, das die Anzahl der Zeilen am schnellsten einschränkt.
  2. Der Index wird nur verwendet, sofern Sie seine Spalten beginnend am Anfang nacheinander verwenden . Wenn Sie also indizieren [:user_id, :article_id], können Sie eine schnelle Abfrage für user_idoder ausführen user_id AND article_id, jedoch NICHT für article_id.

Ihre Migrationslinie add_indexsollte ungefähr so ​​aussehen:

add_index :user_views, [:user_id, :article_id]

Frage zur "einzigartigen" Option

Eine einfache Möglichkeit, dies in Rails zu tun, besteht darin, validatesin Ihrem Modell uniquenessFolgendes zu verwenden ( Dokumentation ):

validates :user, uniqueness: { scope: :article }
sscirrus
quelle
7
Die Reihenfolge spielt bei der Indizierung eine enorme Rolle. Platzieren Sie die where-Klauseln links und vervollständigen Sie den Index mit den Ordnungsspalten rechts. stackoverflow.com/questions/6098616/dos-and-donts-for-indexes
Denis de Bernardy
1
Beachten Sie, dass validates_uniqueness_of(und sein Cousin validates uniqueness:) anfällig für Rennbedingungen sind
Ben Aubin
1
Wie in den obigen Kommentaren und in stackoverflow.com/a/1449466/5157706 und stackoverflow.com/a/22816105/5157706 erwähnt , sollten Sie auch einen eindeutigen Index für die Datenbank hinzufügen.
Akash Agarwal
25

Nur eine Warnung zur Überprüfung der Eindeutigkeit zum Zeitpunkt der Validierung im Vergleich zum Index: Letzteres wird von der Datenbank ausgeführt, während der Primer vom Modell ausgeführt wird. Da möglicherweise mehrere Instanzen eines Modells gleichzeitig ausgeführt werden, unterliegt die Validierung den Rennbedingungen. Dies bedeutet, dass in einigen Fällen möglicherweise keine Duplikate erkannt werden (z. B. zweimal zur gleichen Zeit dasselbe Formular einreichen).

Olivier
quelle
Welches ist also besser? Datenbankseite oder validates_uniqueness_of?
WM
9
Beide. validates_uniqueness_of kann verwendet werden, um eine Fehlermeldung in der Anwendung ordnungsgemäß anzuzeigen, beispielsweise wenn ein Formular gespeichert wird. Durch die Datenbankbeschränkung wird sichergestellt, dass Sie nicht mit Dup-Datensätzen enden, auch wenn Sie wissen, dass im Modell eine Validierung angegeben wurde. Außerdem können Sie die ActiveRecord-Ausnahme retten und dem Benutzer eine nette Nachricht anzeigen.
Uģis Ozols
5
@WM Wenn Sie eine auswählen müssen, gehen Sie mit der Datenbankbeschränkung. Dies funktioniert auch dann, wenn verschiedene Nicht-RoR-Anwendungen mit Ihren Daten interagieren, und stellt langfristig die Konsistenz sicher.
festgemacht