创建一个按点数加权的随机选择,SQL

时间:2014-11-17 11:01:01

标签: sql sql-server

我有一张抽奖的获奖者名单,其中每位获奖者在一年中获得了多个积分。有1300名注册用户,积分在50到43,000之间。我需要能够选择一个随机的赢家,这是直截了当的,但我所面临的挑战是建立逻辑,其中每个点都算作抽奖的入场券。非常感谢任何帮助。

约翰

4 个答案:

答案 0 :(得分:0)

您的脚本看起来与此类似:

脚本1:

DECLARE @Name   varchar(100),
@Points int,
@i      int


DECLARE Cursor1 CURSOR FOR SELECT Name, Points FROM Table1

OPEN Cursor1
FETCH NEXT FROM Cursor1
INTO @Name, @Points
WHILE @@FETCH_STATUS = 0
 BEGIN
  SET @i = 0

  WHILE @i < @Points
   BEGIN
     INSERT INTO Table2 (Name)
     VALUES (@Name)
     SET @i = @i + 1
   END

FETCH NEXT FROM Cursor1 INTO @Name, @Points
 END
DEALLOCATE Cursor1

我创建了一个只有Name和Points列(varchar(100)和int)的表(Table1),我创建了一个游标,以便查看Table1中的所有记录,然后循环遍历Points然后将每条记录插入另一张表(表2)。

然后根据Points列导入Name。

脚本2:

DECLARE @Name   varchar(100),
        @Points int,
        @i      int,
        @Count  int


CREATE TABLE #temptable(
UserEmailID nvarchar(200),
Points int)

DECLARE Cursor1 CURSOR FOR SELECT UserEmailID, Points FROM Table1_TEST

OPEN Cursor1
FETCH NEXT FROM Cursor1
INTO @Name, @Points
WHILE @@FETCH_STATUS = 0
BEGIN
    SET @i = 0
    WHILE @i < @Points
        BEGIN
            INSERT INTO #temptable (UserEmailID, Points)
            VALUES (@Name, @Points)
            SET @i = @i + 1
        END


FETCH NEXT FROM Cursor1 INTO @Name, @Points
END
DEALLOCATE Cursor1

SELECT * FROM #temptable
DROP TABLE #temptable

在Script2中,我已根据请求将结果导入TEMP表。 该脚本现在遍历Table1中的每条记录,并根据表1中的点数将个人UserEmailID和Points导入TEMP表。

因此,如果John总共得到3分,而Sarah 2,脚本会将Johns UserEmailID导入TEMP表3次,将Sarah导入2次。

如果在TEMP表上应用随机选择器,它将随机选择一个人。 John显然有更好的获胜机会,因为他在TEMP表中有3条记录,而Sarah只有2条记录。

假设Johns UserEmailID为1而Sarah为2: 然后,TEMP表的OUTPUT为:

UserEmailID   |  Points
1             | 3
1             | 3
1             | 3
2             | 2
2             | 2

如果您需要清楚,请告诉我。 希望这会有所帮助。

答案 1 :(得分:0)

您可以使用以下方法进行加权抽奖:

  • 计算累计点数。
  • 除以总点数以获得介于0和1之间的值
  • 原始数据中的每一行都有一个范围,例如[0,0.1),[0.1,0.3),[0.3,1]
  • 计算随机数并选择值落在
  • 范围内的行

以下是此方法的标准SQL:

with u as (
      select u.*,
             coalesce(lead(rangestart) over (order by points) as rangeend, 1)
      from (select u.*,
                   sum(points*1.0) over (order by points) / sum(points) over () as rangestart
            from users u
           ) u
     ),
     r as (
      select random() as rand
     )
select u.*
from u
where r.rand between rangestart and rangeend;

除了使用窗口函数(在很多情况下可以由相关子查询处理)之外,确切的格式取决于随机数生成器对查询是否具有确定性(例如random()返回一个查询的SQL Server值,无论在查询中调用的频率如何)或非确定性(例如在其他数据库中)。此方法只需要一个随机数生成器的值,因此它可以使用任何一种方法。

答案 2 :(得分:0)

所以你想要一个拥有1000分的胜利者,只有500分的可能性增加一倍。

按任何顺序对获胜者进行排序,并为这些点创建一个总计:

id       points
winner1  100
winner2  50
winner3  150

给出:

id       points    from  to
winner1  100         1   100
winner2   50       101   150
winner3  150       151   300

然后与从1到总和(点数)的随机数进行比较,在示例中为1到300之间的数字。找到具有该数字范围的获胜者并完成。

select winpoints.id_winner
from
(
  select 
    id as id_winner,
    coalesce(sum(points) over(order by id rows between unbounded preceding and 1 preceding), 0) + 1 as from_points,
    sum(points) over(order by id rows between unbounded preceding and current row) as to_points
  from winners
) winpoints
where (select floor(rand() * (sum(points) from winners)) + 1)
      between winpoints.from_points and winpoints.to_points;

答案 3 :(得分:0)

此解决方案也适用于分数点/权重。它创建了一个帮助表usersum

create table user (id int primary key, points float);
insert into user values (1, 0.5), (2, 0), (3, 1);

create table usersum (id int primary key, pointsum float);
insert into usersum
select id, (select sum(points) from user b where b.id <= a.id)
from user a;

set @r = rand() * (select max(pointsum) from usersum);
select @r, usersum.* from usersum where pointsum >= @r order by id limit 1;

http://sqlfiddle.com/#!2/ae539e/1