Danes svar inkluderar ett jag går med på ett sätt som introducerar en kvadratisk lag. (n*n/2)
rader efter sammanfogningen där det finns n rader i tabellen.
Vad som skulle vara mer idealiskt är att bara kunna analysera tabellen en gång.
DECLARE @id int, @weight_sum int, @weight_point int
DECLARE @table TABLE (id int, weight int)
INSERT INTO @table(id, weight) VALUES(1, 50)
INSERT INTO @table(id, weight) VALUES(2, 25)
INSERT INTO @table(id, weight) VALUES(3, 25)
SELECT @weight_sum = SUM(weight)
FROM @table
SELECT @weight_point = FLOOR(((@weight_sum - 1) * RAND() + 1))
SELECT
@id = CASE WHEN @weight_point < 0 THEN @id ELSE [table].id END,
@weight_point = @weight_point - [table].weight
FROM
@table [table]
ORDER BY
[table].Weight DESC
Detta går igenom tabellen och ställer in @id
till varje posts id
värde samtidigt som @weight
minskar punkt. Så småningom, @weight_point
kommer att bli negativ. Det betyder att SUM
av alla föregående vikter är större än det slumpmässigt valda målvärdet. Det här är posten vi vill ha, så från den punkten och framåt ställer vi in @id
till sig själv (ignorerar eventuella ID:n i tabellen).
Detta går igenom tabellen bara en gång, men måste gå igenom hela tabellen även om det valda värdet är den första posten. Eftersom den genomsnittliga positionen är halvvägs genom tabellen (och mindre om den sorteras efter stigande vikt) kan det möjligen gå snabbare att skriva en loop... (Speciellt om viktningarna är i gemensamma grupper):
DECLARE @id int, @weight_sum int, @weight_point int, @next_weight int, @row_count int
DECLARE @table TABLE (id int, weight int)
INSERT INTO @table(id, weight) VALUES(1, 50)
INSERT INTO @table(id, weight) VALUES(2, 25)
INSERT INTO @table(id, weight) VALUES(3, 25)
SELECT @weight_sum = SUM(weight)
FROM @table
SELECT @weight_point = ROUND(((@weight_sum - 1) * RAND() + 1), 0)
SELECT @next_weight = MAX(weight) FROM @table
SELECT @row_count = COUNT(*) FROM @table WHERE weight = @next_weight
SET @weight_point = @weight_point - (@next_weight * @row_count)
WHILE (@weight_point > 0)
BEGIN
SELECT @next_weight = MAX(weight) FROM @table WHERE weight < @next_weight
SELECT @row_count = COUNT(*) FROM @table WHERE weight = @next_weight
SET @weight_point = @weight_point - (@next_weight * @row_count)
END
-- # Once the @weight_point is less than 0, we know that the randomly chosen record
-- # is in the group of records WHERE [table].weight = @next_weight
SELECT @row_count = FLOOR(((@row_count - 1) * RAND() + 1))
SELECT
@id = CASE WHEN @row_count < 0 THEN @id ELSE [table].id END,
@row_count = @row_count - 1
FROM
@table [table]
WHERE
[table].weight = @next_weight
ORDER BY
[table].Weight DESC