[英]How to dynamically perform a weighted random row selection in PostgreSQL?
我有一個應用程序的下表,其中學生被分配任務來玩教育游戲。
學生{id,last_played_datetime,total_play_duration,total_points_earned}
該應用程序隨機選擇一名學生並分配任務。 學生只需玩游戲即可獲得一分。 該應用程序記錄玩游戲的日期和時間以及持續時間。 我想隨機 select 一個學生並分配任務。 一次只能為一名學生分配任務。 為了給所有學生平等的機會,我正在使用學生上次玩游戲的日期和時間、總游戲時間和學生獲得的總積分動態計算學生的體重。 然后將隨機選擇一個受體重影響的學生。
我如何在 PostgreSQL 中根據動態計算的行權重從表中隨機 select 行?
每個學生的權重計算如下:(分鍾(current_datetime - last_played_datetime) * 0.75 + total_play_duration * 0.5 + total_points_earned * 0.25) / 1.5
樣本數據:
+====+======================+=====================+=====================+
| Id | last_played_datetime | total_play_duration | total_points_earned |
+====+======================+=====================+=====================+
| 1 | 01/02/2011 | 300 mins | 7 |
+----+----------------------+---------------------+---------------------+
| 2 | 06/02/2011 | 400 mins | 6 |
+----+----------------------+---------------------+---------------------+
| 3 | 01/03/2011 | 350 mins | 8 |
+----+----------------------+---------------------+---------------------+
| 4 | 22/03/2011 | 550 mins | 9 |
+----+----------------------+---------------------+---------------------+
| 5 | 01/03/2011 | 350 mins | 8 |
+----+----------------------+---------------------+---------------------+
| 6 | 10/01/2011 | 130 mins | 2 |
+----+----------------------+---------------------+---------------------+
| 7 | 03/01/2011 | 30 mins | 1 |
+----+----------------------+---------------------+---------------------+
| 8 | 07/10/2011 | 0 mins | 0 |
+----+----------------------+---------------------+---------------------+
這是一個工作原理如下的解決方案:
詢問:
with
student_with_weight as (
select
id,
(
extract(epoch from (now() - last_played_datetime)) / 60 * 0.75
+ total_play_duration * 0.5
+ total_points_earned * 0.25
) / 1.5 weight
from student
),
random_weight as (
select random() * (select sum(weight) weight from student_with_weight ) weight
)
select id
from
student_with_weight s
inner join random_weight r on s.weight >= r.weight
order by id
limit 1;
您可以對權重使用累積總和並與rand()
進行比較。 它看起來像這樣:
with s as (
select s.*,
<your expression> as weight
from s
)
select s.*
from (select s.*,
sum(weight) over (order by weight) as running_weight,
sum(weight) over () as total_weight
from s
) s cross join
(values (random())) r(rand)
where r.rand * total_weight >= running_weight - weight and
r.rand * total_weight < running_weight;
values()
子句確保隨機值只為查詢計算一次。 如果將random()
放在where
子句中,可能會發生一些奇怪的事情,因為每次比較都會重新計算它。
基本上,您可以將累積總和視為將總數划分為離散區域。 然后rand()
只是選擇其中之一。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.