Column Down
select
och pluck
är två olika och användbara sätt att returnera värdena från en eller flera kolumner i en databastabell som en samling. Tänk dig till exempel att du har en questions
-tabell i din databas. Varje question
kan ha tre fält: id
, question_text
och ett foreign_key
från en associerad tabell. Om du vill returnera värdena från endast kolumnen question_text
skulle du använda select
eller pluck
. Jag vill utforska skillnaderna mellan dessa två metoder och avsluta med en tillämpning av pluck
i en Rails-applikation.
Select
select
fungerar på två unika sätt:
i.select
tar ett block och fungerar precis som Array#select
.
Model.all.select { |m| m.field == value }
Detta bygger upp en array av Ruby-objekt från databasen för räckvidden, omvandlar dem till en array och itererar genom dem med hjälp av Array#select
.
select
vswhere
Förmedla ett block till
select
med en stor datamängd (dvs. många rader) är långsammare än att använda andra strategier, somwhere
:
Model.where(field: value)
för att med
Model.all.select { |m| m.field == value }
konverterar Rails först alla tabellrader till Ruby-objekt och kör sedan det givna blocket för varje Ruby-objekt, medanModel.where(field: value)
genererar ett SQL-uttalande som avlastar SQL från det tunga arbetet, vilket är snabbare än Ruby för den här typen av filtrering.
ii. select
ändrar SELECT
-angivelsen för SQL-frågan så att endast vissa fält hämtas:
Model.select(:field)
# =>
Till exempel från ett av mina projekt:
Question.select(:question_text)
# => #<ActiveRecord::Relation >
Detta genererar följande SQL:
SELECT "questions"."question_text" FROM "questions"
Bemärk att i det här andra exemplet returnerar select
-metoden inte en matris, den returnerar ett ActiveRecord::Relation-objekt. Därför kan du kedja andra ActiveRecord::QueryMethods till den. Till exempel:
Question.select(:question_text).limit(5)# => #<ActiveRecord::Relation >
Detta genererar följande SQL:
SELECT "questions"."question_text" FROM "questions" LIMIT ? ]
Pluck
Från Rails Guides:
pluck
kan användas för att fråga efter en eller flera kolumner från den underliggande tabellen i en modell. Den tar emot en lista med kolumnnamn som argument och returnerar en array av värden för de angivna kolumnerna med motsvarande datatyp.
Den viktiga punkten här är att pluck
returnerar en array. Den returnerar inte ett ActiveRecord::Relation-objekt, som select
. Så, till exempel:
Question.pluck(:question_text)# => # this returns an array with all of the values from the question_text column from the questions table.
Då pluck
omvandlar resultatet av en databasfråga till en Ruby-array utan att konstruera ett ActiveRecord
-objekt är den mer effektiv än select
.
Då pluck
returnerar en array innebär det dock att du inte kan kedja standard ActiveRecord
-frågor på pluck
. Om du gör det kommer du att få en NoMethodError
:
Question.pluck(:question_text).limit(5)# => NoMethodError: undefined method `limit' for #<Array:0x007fc5c1dfb3b0>
Men pluck
fungerar på ActiveRecord
-objekt, så följande kommer att ge det förväntade resultatet:
Question.limit(5).pluck(:question_text)#=>
pluck
kan ta emot flera argument för att konstruera en flerdimensionell array:
Question.pluck(:question_text, :course_id)#=> , , ...]
När skulle jag använda Pluck?
Jag har använt pluck
för att avgränsa ActiveRecord
modellobjekt för att avgöra vilka modellobjekt som har associerade poster i en annan tabell.
Titta till exempel följande klass:
class Appointment < ActiveRecord::Base
has_many :chargesend
Om vi vill avgöra vilka appointments
som har charges
och vilka som inte har det, kan vi använda pluck
i en scope
. Så här gör vi:
För det första ska du komma ihåg att för Model.where(field: value)
kan value
antingen vara ett enskilt objekt (som integer
, string
etc.), eller så kan det vara en array
av objekt.
Så följande är giltigt och returnerar alla appointments
med ids
som ingår i matrisen.
Appointment.where(id: )# => returns all appointments with ids that are included in the array
Vi vet att pluck
returnerar en array, så vi kan konstruera en array som innehåller de främmande nycklar som vi vill ha räckvidd för i den associerade modellen. Om vi till exempel vill bestämma vilka appointments
som har associerat charges
kan vi skapa en array från kolumnen appointment_id
i tabellen charges
, så här:
Charge.pluck(:appointment_id)# => returns an array with all of the appointment_ids from the charges table, like:
För att bestämma vilka appointments
som har associerade avgifter kan vi skriva:
Appointment.where(id: Charge.pluck(:appointment_id))# this is the same as Appointment.where(id: )
Och konvertera detta till ett scope på Appointment
-modellen:
class Appointment < ActiveRecord::Base
has_many :charges scope :received_payment_info, -> {
where(id: Charge.pluck(:appointment_id)) }end
Så när vi nu vill bestämma vilka appointments
som har associerade charges
, kan vi skriva:
Appointment.received_payment_info#=> #<ActiveRecord::Relation
vilket genererar följande SQL-frågor:
SELECT "charges"."appointment_id" FROM "charges"SELECT "appointments".* FROM "appointments" WHERE ("appointments"."id" IN (35, 44, 82) OR "appointments"."id" IS NULL)
pluck
vilket gör att vi kan:
- på ett effektivt sätt fråga en associerad databastabell för att…
- generera en matris från den associerade tabellen med alla foreign_keys för objekt från den primära tabellen
- använda den matrisen för att avgöra vilka objekt i den primära tabellen som har associerade objekt i den sekundära tabellen
Jag hoppas att du tyckte att det här var till hjälp. Lycka till med att plocka och välja ut.