Índice en varias columnas en Ruby on Rails

97

Estoy implementando una funcionalidad para rastrear qué artículos ha leído un usuario.

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

Esta es mi migración hasta ahora:

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

Siempre se consultará la tabla user_views para buscar ambas columnas, nunca solo una. Mi pregunta es cómo debería verse mi índice. ¿Hay alguna diferencia en el orden de estas tablas, debería haber más opciones o lo que sea? Mi base de datos de destino es Postgres.

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

Gracias.

ACTUALIZACIÓN:
Debido a que solo puede existir una fila que contenga los mismos valores en ambas columnas (ya que al saber si user_id HA leído article_id), ¿debería considerar la opción: única? Si no me equivoco, eso significa que no tengo que hacer ninguna verificación por mi cuenta y simplemente hacer una inserción cada vez que un usuario visita un artículo.

Emil Ahlbäck
fuente
"Siempre se consultará la tabla user_views para buscar ambas columnas, nunca solo una". - ¿Nunca habrá una consulta "buscar todos los artículos que este usuario ha visto" o "buscar todos los usuarios que han visto este artículo"? Eso me sorprende.
David Aldridge

Respuestas:

212

El orden sí importa en la indexación.

  1. Coloque primero el campo más selectivo, es decir, el campo que reduce el número de filas más rápido.
  2. El índice solo se utilizará en la medida en que utilice sus columnas en secuencia comenzando por el principio . es decir, si indexa [:user_id, :article_id], puede realizar una consulta rápida en user_ido user_id AND article_id, pero NO en article_id.

Su add_indexlínea de migración debería verse así:

add_index :user_views, [:user_id, :article_id]

Pregunta sobre la opción 'única'

Una manera fácil de hacer esto en Rails es usarlo validatesen su modelo con el alcance de la uniquenesssiguiente manera ( documentación ):

validates :user, uniqueness: { scope: :article }
sscirrus
fuente
7
El orden importa enormemente en la indexación. Coloque las cláusulas where a la izquierda y complete el índice con las columnas de orden a la derecha. stackoverflow.com/questions/6098616/dos-and-donts-for-indexes
Denis de Bernardy
1
Tenga en cuenta que validates_uniqueness_of(y su primo validates uniqueness:) son propensos a las condiciones de carrera
Ben Aubin
1
Como se mencionó en los comentarios anteriores y en stackoverflow.com/a/1449466/5157706 y stackoverflow.com/a/22816105/5157706 , considere agregar un índice único en la base de datos también.
Akash Agarwal
25

Solo una advertencia sobre la verificación de la unicidad en el momento de la validación frente al índice: la base de datos realiza esta última, mientras que el modelo realiza la cartilla. Dado que puede haber varias instancias simultáneas de un modelo ejecutándose al mismo tiempo, la validación está sujeta a las condiciones de la carrera, lo que significa que puede no detectar duplicados en algunos casos (por ejemplo, enviar dos veces el mismo formulario al mismo tiempo).

olivier
fuente
¿Entonces cual es mejor? Lado de la base de datos o validates_uniqueness_of?
WM
9
Ambos. validates_uniqueness_of se puede utilizar para mostrar un mensaje de error con elegancia en la aplicación, por ejemplo, cuando se guarda un formulario. La restricción de la base de datos aseguraría que no termine con registros dup, incluso si sabe que tiene la validación especificada en el modelo. Además, puede rescatar la excepción ActiveRecord y también mostrar un mensaje agradable al usuario.
Uģis Ozols
5
@WM Si tiene que elegir uno, siga la restricción de la base de datos. Esto funcionará incluso si diferentes aplicaciones que no son de RoR interactúan con sus datos y garantiza la coherencia a largo plazo.
amarrado el