Column Down
select
y pluck
son dos formas diferentes y útiles de devolver los valores de una o más columnas de una tabla de la base de datos como una colección. Por ejemplo, imagine que tiene una tabla questions
en su base de datos. Cada question
podría tener tres campos: id
, question_text
y un foreign_key
de una tabla asociada. Para devolver los valores de sólo la columna question_text
, utilizaría select
o pluck
. Me gustaría explorar las diferencias entre estos dos métodos y terminar con una aplicación de pluck
dentro de una aplicación Rails.
Select
select
funciona de dos maneras únicas:
i.select
toma un bloque y funciona igual que Array#select
.
Model.all.select { |m| m.field == value }
Esto construye un array de objetos Ruby desde la base de datos para el ámbito, convirtiéndolos en un array e iterando a través de ellos usando Array#select
.
select
vswhere
Pasar un bloque a
select
con un conjunto de datos grande (es decir muchas filas) es más lento que utilizar otras estrategias, comowhere
:
Model.where(field: value)
porque con
Model.all.select { |m| m.field == value }
, Rails convertirá primero todas las filas de la tabla en objetos Ruby, y luego ejecutará el bloque dado para cada objeto Ruby, mientras queModel.where(field: value)
genera una sentencia SQL que descarga el trabajo pesado a SQL, que es más rápido que Ruby para este tipo de filtrado.
ii. select
modifica la sentencia SELECT
de la consulta SQL para que sólo se recuperen ciertos campos:
Model.select(:field)
# =>
Por ejemplo, de un proyecto mío:
Question.select(:question_text)
# => #<ActiveRecord::Relation >
Esto genera el siguiente SQL:
SELECT "questions"."question_text" FROM "questions"
Tenga en cuenta que en este segundo ejemplo, el método select
no devuelve un array; devuelve un objeto ActiveRecord::Relation. Como resultado, puedes encadenar otros ActiveRecord::QueryMethods a él. Por ejemplo:
Question.select(:question_text).limit(5)# => #<ActiveRecord::Relation >
Esto genera el siguiente SQL:
SELECT "questions"."question_text" FROM "questions" LIMIT ? ]
Pluck
De las guías de Rails:
pluck
puede utilizarse para consultar una o varias columnas de la tabla subyacente de un modelo. Acepta una lista de nombres de columnas como argumento y devuelve un array de valores de las columnas especificadas con el tipo de datos correspondiente.
El punto importante aquí es que pluck
devuelve un array. No devuelve un objeto ActiveRecord::Relation, como select
. Así, por ejemplo:
Question.pluck(:question_text)# => # this returns an array with all of the values from the question_text column from the questions table.
Debido a que pluck
convierte el resultado de una consulta a la base de datos en una matriz de Ruby sin construir un objeto ActiveRecord
, es más eficaz que select
.
Sin embargo, el hecho de que pluck
devuelva una matriz significa que no se pueden encadenar consultas estándar ActiveRecord
en pluck
. Hacerlo devolverá un NoMethodError
:
Question.pluck(:question_text).limit(5)# => NoMethodError: undefined method `limit' for #<Array:0x007fc5c1dfb3b0>
Pero pluck
sí funciona con objetos ActiveRecord
, así que lo siguiente devolverá el resultado esperado:
Question.limit(5).pluck(:question_text)#=>
pluck
puede tomar múltiples argumentos para construir una matriz multidimensional:
Question.pluck(:question_text, :course_id)#=> , , ...]
¿Cuándo utilizaría Pluck?
He utilizado pluck
para el alcance de ActiveRecord
objetos del modelo para determinar qué objetos del modelo tienen registros asociados en otra tabla.
Por ejemplo, tome la siguiente clase:
class Appointment < ActiveRecord::Base
has_many :chargesend
Si queremos determinar qué appointments
tienen charges
y cuáles no, podemos utilizar pluck
en un scope
. He aquí cómo:
Primero, recuerda que para Model.where(field: value)
, value
puede ser un solo objeto (como integer
, string
, etc.), o puede ser un array
de objetos.
Así, lo siguiente es válido y devolverá todos los appointments
con ids
que estén incluidos en el array.
Appointment.where(id: )# => returns all appointments with ids that are included in the array
Bueno, sabemos que pluck
devuelve un array, por lo que podemos construir un array que incluya las claves foráneas que queremos abarcar en el modelo asociado. Por ejemplo, si queremos determinar qué appointments
tienen asociado charges
, podemos crear un array a partir de la columna appointment_id
de la tabla charges
, así:
Charge.pluck(:appointment_id)# => returns an array with all of the appointment_ids from the charges table, like:
Para determinar qué appointments
tienen cargos asociados, podemos escribir:
Appointment.where(id: Charge.pluck(:appointment_id))# this is the same as Appointment.where(id: )
Y convirtiendo esto en un ámbito en el modelo Appointment
:
class Appointment < ActiveRecord::Base
has_many :charges scope :received_payment_info, -> {
where(id: Charge.pluck(:appointment_id)) }end
Así que ahora cuando queramos determinar qué appointments
tienen asociados charges
, podemos escribir:
Appointment.received_payment_info#=> #<ActiveRecord::Relation
lo que genera las siguientes consultas SQL:
SELECT "charges"."appointment_id" FROM "charges"SELECT "appointments".* FROM "appointments" WHERE ("appointments"."id" IN (35, 44, 82) OR "appointments"."id" IS NULL)
pluck
nos permite:
- consultar de forma eficiente una tabla de la base de datos asociada para…
- generar un array de la tabla asociada con todas las foreign_keys de los objetos de la tabla primaria
- utilizar ese array para determinar qué objetos de la tabla primaria tienen objetos asociados en la tabla secundaria
Espero que os haya resultado útil. Disfruta desplumando y seleccionando.