La réponse de Dane inclut une auto-jointure d'une manière qui introduit une loi carrée. (n*n/2)
lignes après la jointure où il y a n lignes dans la table.
L'idéal serait de pouvoir analyser la table une seule fois.
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
Cela passera par le tableau, en définissant @id
à l'id
de chaque enregistrement value tout en décrémentant @weight
indiquer. Finalement, le @weight_point
deviendra négatif. Cela signifie que la SUM
de tous les poids précédents est supérieur à la valeur cible choisie au hasard. C'est l'enregistrement que nous voulons, donc à partir de ce moment, nous définissons @id
à lui-même (en ignorant tous les ID dans le tableau).
Cela parcourt la table une seule fois, mais doit parcourir toute la table même si la valeur choisie est le premier enregistrement. Parce que la position moyenne est à mi-chemin dans le tableau (et moins si ordonnée par poids croissant), l'écriture d'une boucle pourrait éventuellement être plus rapide... (surtout si les pondérations sont dans des groupes communs) :
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