Buat indeks pada banyak kolom di Ruby on Rails

97

Saya menerapkan fungsionalitas untuk melacak artikel mana yang telah dibaca pengguna.

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

Ini migrasi saya sejauh ini:

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

Tabel user_views akan selalu ditanyai untuk mencari kedua kolom, tidak hanya satu. Pertanyaan saya adalah bagaimana tampilan indeks saya. Apakah ada perbedaan dalam urutan tabel ini, apakah ada lebih banyak pilihan untuk itu atau apapun. DB target saya adalah Postgres.

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

Terima kasih.

UPDATE:
Karena hanya satu baris yang berisi nilai yang sama di kedua kolom yang dapat ada (karena dengan mengetahui apakah user_id TELAH membaca article_id), haruskah saya mempertimbangkan opsi: unik? Jika saya tidak salah, itu berarti saya tidak perlu melakukan pemeriksaan sendiri dan cukup membuat sisipan setiap kali pengguna mengunjungi artikel.

Emil Ahlbäck
sumber
"Tabel user_views akan selalu ditanyai untuk mencari kedua kolom, tidak hanya satu." - tidak akan pernah ada kueri "temukan semua artikel yang telah dilihat pengguna ini", atau "temukan semua pengguna yang telah melihat artikel ini"? Saya merasa itu mengejutkan.
David Aldridge

Jawaban:

212

Urutan penting dalam pengindeksan.

  1. Letakkan bidang paling selektif terlebih dahulu, yaitu bidang yang mempersempit jumlah baris tercepat.
  2. Indeks hanya akan digunakan selama Anda menggunakan kolomnya secara berurutan mulai dari awal . yaitu jika Anda mengindeks [:user_id, :article_id], Anda dapat melakukan kueri cepat pada user_idatau user_id AND article_id, tetapi TIDAK aktif article_id.

add_indexGaris migrasi Anda akan terlihat seperti ini:

add_index :user_views, [:user_id, :article_id]

Pertanyaan tentang opsi 'unik'

Cara mudah untuk melakukan ini di Rails adalah dengan menggunakan validatesmodel Anda dengan cakupan uniquenesssebagai berikut ( dokumentasi ):

validates :user, uniqueness: { scope: :article }
sscirrus.dll
sumber
7
Urutan sangat penting dalam pengindeksan. Letakkan klausa where di sebelah kiri dan selesaikan indeks dengan kolom pengurutan di sebelah kanan. stackoverflow.com/questions/6098616/dos-and-donts-for-indexes
Denis de Bernardy
1
Perhatikan bahwa validates_uniqueness_of(dan sepupunya, validates uniqueness:) rentan terhadap kondisi balapan
Ben Aubin
1
Seperti disebutkan dalam komentar di atas dan stackoverflow.com/a/1449466/5157706 dan stackoverflow.com/a/22816105/5157706 , pertimbangkan juga untuk menambahkan indeks unik pada database.
Akash Agarwal
25

Hanya peringatan tentang memeriksa keunikan pada waktu validasi vs. pada indeks: yang terakhir dilakukan oleh database sedangkan primer dilakukan oleh model. Karena mungkin ada beberapa contoh bersamaan dari model yang berjalan pada waktu yang sama, validasi tunduk pada kondisi balapan, yang berarti mungkin gagal mendeteksi duplikat dalam beberapa kasus (misalnya, mengirimkan formulir yang sama dua kali pada waktu yang sama).

olivier
sumber
Jadi mana yang lebih baik? Sisi database atau validates_uniqueness_of?
WM
9
Kedua. validates_uniqueness_of dapat digunakan untuk menampilkan pesan kesalahan dengan baik di aplikasi misalnya ketika formulir disimpan. Batasan database akan memastikan Anda tidak berakhir dengan record dup bahkan tahu Anda memiliki validasi yang ditentukan dalam model. Selain itu, Anda dapat menyelamatkan pengecualian ActiveRecord dan juga menampilkan pesan yang bagus kepada pengguna.
Uģis Ozols
5
@WM Jika Anda harus memilih salah satu, ikuti batasan database. Ini akan bekerja bahkan jika berbeda, aplikasi non RoR berinteraksi dengan data Anda, dan memastikan konsistensi untuk jangka panjang.
ditambatkan