簡體   English   中英

從每個類別中選擇一個的有效方法 - Rails

[英]Efficient way to select one from each category - Rails

我正在開發一個簡單的應用程序返回的隨機選擇exercises ,每一個bodypart

bodypartExercise模型上的索引enum列。 DB是PostgreSQL。

以下實現了我想要的結果,但感覺非常低效(每個bodypart擊中db一次):

BODYPARTS = %w(legs core chest back shoulders).freeze

@exercises = BODYPARTS.map do |bp|
  Exercise.public_send(bp).sample
end.shuffle

因此,這為每個exercise bodypart提供隨機exercise ,並在最后混合順序。

我還可以將所有練習存儲在內存中並從中選擇; 但是,我想這會有可怕的擴展(目前只有十幾個種子記錄)。

@exercises = Exercise.all

BODYPARTS.map do |bp|
  @exercises.select { |e| e[:bodypart] == bp }.sample
end.shuffle

對這些進行基准測試表明, select方法在小范圍內更有效:

Queries:            0.072902   0.020728   0.093630 (  0.088008)
Select:             0.000962   0.000225   0.001187 (  0.001113)
MrYoshiji's answer: 0.000072   0.000008   0.000080 (  0.000072)

我的問題是,是否有一種有效的方法來實現這一輸出,如果是,那么這種方法可能是什么樣子。 理想情況下,我想將其保留為單個數據庫查詢。

很高興使用ActiveRecord或直接在SQL中撰寫。 任何想法都非常感激。

從我的評論中,你應該能夠做到(感謝PostgreSQL的DISTINCT ON ):

Exercise.select('distinct on (bodypart) *')
        .order('bodypart, random()')

Postgres的' DISTINCT ON非常方便, 性能通常也很好 - 對於許多不同的身體部位,每個部分都很少 但是只有少數不同的bodypart值,每個都有很多行(大表 - 和你的用例),有很多優秀的查詢技術。

在這種情況下,這將大大加快:

SELECT e.*
FROM   unnest(enum_range(null::bodypart)) b(bodypart)
CROSS  JOIN LATERAL (
   SELECT *
   FROM   exercises
   WHERE  bodypart = b.bodypart
   -- ORDER BY ??? -- for a deterministic pick
   LIMIT  1        -- arbitrary pick!
   ) e;

假設bodypart是枚舉的名稱以及表列。

enum_range是一個枚舉支持函數 (引用手冊):

返回有序數組中輸入枚舉類型的所有值

我不需要它並為每個值運行一個LATERAL子查詢,當使用正確的索引支持時,它非常快。 查詢技術和所需索引的詳細說明(關注章節“2a.LATERAL join” ):

對於每個bodypart的任意行,一個簡單的exercises(bodypart)索引exercises(bodypart)就可以完成這項工作。 但是你可以使用正確的多列索引和匹配的ORDER BY子句以及幾乎相同的性能來獲得像“最新條目”這樣的確定性選擇。 有關:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM