[英]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.