Indice su più colonne in Ruby on Rails


97

Sto implementando funzionalità per tenere traccia degli articoli letti da un utente.

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

Questa è la mia migrazione finora:

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

La tabella user_views verrà sempre interrogata per cercare entrambe le colonne, mai solo una. La mia domanda è come dovrebbe apparire il mio indice. C'è una differenza nell'ordine di queste tabelle, dovrebbe esserci qualche altra opzione o altro. Il mio DB di destinazione è Postgres.

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

Grazie.

AGGIORNAMENTO:
Poiché può esistere solo una riga contenente gli stessi valori in entrambe le colonne (poiché sapendo se user_id HA letto article_id), dovrei considerare l'opzione: unique? Se non sbaglio significa che non devo fare alcun controllo da solo e semplicemente fare un inserto ogni volta che un utente visita un articolo.


"La tabella user_views verrà sempre interrogata per cercare entrambe le colonne, mai solo una." - non ci sarà mai una query "trova tutti gli articoli che questo utente ha visualizzato" o "trova tutti gli utenti che hanno visualizzato questo articolo"? Lo trovo sorprendente.
David Aldridge

Risposte:


212

L'ordine è importante nell'indicizzazione.

  1. Metti per primo il campo più selettivo, cioè il campo che restringe il numero di righe più velocemente.
  2. L'indice verrà utilizzato solo nella misura in cui si utilizzano le sue colonne in sequenza a partire dall'inizio . cioè se indicizzi su [:user_id, :article_id], puoi eseguire una query veloce su user_ido user_id AND article_id, ma NON su article_id.

La tua add_indexlinea di migrazione dovrebbe essere simile a questa:

add_index :user_views, [:user_id, :article_id]

Domanda riguardante l'opzione "unica"

Un modo semplice per farlo in Rails è usare validatesnel tuo modello con scope uniquenesscome segue ( documentazione ):

validates :user, uniqueness: { scope: :article }

7
L'ordine conta enormemente nell'indicizzazione. Posiziona le clausole where a sinistra e completa l'indice con le colonne di ordinamento a destra. stackoverflow.com/questions/6098616/dos-and-donts-for-indexes
Denis de Bernardy,

1
Nota che validates_uniqueness_of(e suo cugino validates uniqueness:) sono inclini alle condizioni di gara
Ben Aubin

1
Come menzionato nei commenti sopra e stackoverflow.com/a/1449466/5157706 e stackoverflow.com/a/22816105/5157706 , considera anche l'aggiunta di un indice univoco al database.
Akash Agarwal

25

Solo un avvertimento sul controllo dell'unicità al momento della convalida rispetto all'indice: quest'ultimo è fatto dal database mentre il primer è fatto dal modello. Poiché potrebbero esserci più istanze simultanee di un modello in esecuzione contemporaneamente, la convalida è soggetta a condizioni di gara, il che significa che potrebbe non riuscire a rilevare i duplicati in alcuni casi (ad esempio, inviare due volte lo stesso modulo nello stesso identico momento).


Quindi qual è il migliore? Lato database o validates_uniqueness_of?
WM

9
Tutti e due. validates_uniqueness_of può essere utilizzato per visualizzare correttamente un messaggio di errore nell'applicazione, ad esempio quando un modulo viene salvato. Il vincolo del database assicurerebbe che non ti ritroverai con record dup anche se sapevi che la convalida era stata specificata nel modello. Inoltre, puoi salvare l'eccezione ActiveRecord e mostrare anche un bel messaggio all'utente.
Uģis Ozols

5
@WM Se devi sceglierne uno, segui il vincolo del database. Ciò funzionerà anche se diverse applicazioni non RoR interagiscono con i dati e garantisce la coerenza a lungo termine.
ormeggi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.