sql查询选择一个随机值(加权)

时间:2011-04-27 21:48:13

标签: sql sql-server

我有一个包含三个字段的表:ID,Value,Count

ID和价值构成了PK。

给定一个ID,我想选择一个由Count加权的值,然后将计数减一。

所以,如果我有

1  A  2
1  B  3

我应该有2/5的机会获得A和3/5获得B的机会。 如果选择A,则表格在

之后应如下所示
1  A  1
1  B  3

下次A将有1/4的机会被选中而B将有3/4

想法?

谢谢!

更新

重量就像放在包里的那个价值的筹码数量。然后随机选择一个。

3 个答案:

答案 0 :(得分:4)

CREATE TABLE #Vals
(
ID INT,
Value CHAR(1),
[Count] INT,
PRIMARY KEY(ID,Value)
)

INSERT INTO #Vals
SELECT 1,'A',2 UNION ALL
SELECT 1,'B',3

;WITH Nums AS
(
SELECT number 
FROM master..spt_values
WHERE number>0 AND type='P'
), Row AS
(
SELECT TOP 1 v.*
FROM #Vals v
JOIN Nums n ON n.number <= v.[Count]
WHERE v.ID=1
ORDER BY CHECKSUM(NEWID())
)
UPDATE Row 
SET [Count] = [Count] -1
OUTPUT inserted.*


DROP TABLE #Vals

答案 1 :(得分:1)

在MySQL中(也许有人可以把它翻译成MSSQL UPDATE语句?)这会给你需要减少的行:

SELECT *
FROM t
WHERE ID = 1
AND (
  SELECT COALESCE(SUM(Count), 0)
  FROM t i
  WHERE ID = 1
  AND i.Value < t.Value
) <= (
  SELECT FLOOR(SUM(Count) * RAND())
  FROM t
)
ORDER BY Value DESC
LIMIT 1

因为它是O(n²),但对于大型数据集来说它会很慢。

答案 2 :(得分:0)

试试这个(前提是此表中没有太多记录)。我做了这样的结构:

id          value                                              weight
----------- -------------------------------------------------- -----------
1           A                                                  2
1           B                                                  3

然后运行以下代码。请记住,经过一段时间后,您的所有体重都将变为零,此时您将没有返回值。另外,我假设如果没有基于随机回报的权重匹配,则返回最大权重值。如果那不是您想要的,只需取出最后一次空检查


declare @weight_sum int
declare @rand_weight_value int
declare @selected_value varchar(10)
declare @id int
declare @weight int

select @weight_sum = SUM(weight) from table1 where id = 1
select @rand_weight_value = ROUND(((@weight_sum - 1) * RAND() + 1), 0) 

print @rand_weight_value

declare getEm cursor local  for select ID, [value], [weight] from table1 where id = 1 and [weight] > 0 order by [weight]

open getEm
        while (1=1)
        begin
                 fetch next from getEm into @id, @selected_value, @weight

                 if (@@fetch_status  0)
                    begin
                        DEALLOCATE getEm
                        break
                    end


                if(@weight >= @rand_weight_value)
                begin
                        update table1 set [weight] = [weight] - 1 where ID = @id and [value] = @selected_value

                        DEALLOCATE getEm
                        break
                end

        end

-- if no match on the weight value
if(@selected_value is null)
begin
    select @id = id, @selected_value = [value] from table1 where [weight] > 0 and id = 1 order by weight desc
         update table1 set weight = weight - 1 where id = @id
end
select @selected_value