Ответ Дейна включает в себя «Я», который вводит закон квадрата. (n*n/2)
строк после объединения, где в таблице n строк.
Что было бы более идеально, так это возможность только один раз проанализировать таблицу.
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), 0)
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
Это будет проходить через таблицу, устанавливая @id
в значение id
каждой записи, в то же время уменьшая @weight
точку. В конце концов, @weight_point
станет отрицательным. Это означает, что SUM
всех предыдущих весов больше, чем случайно выбранное целевое значение. Это запись, которую мы хотим, поэтому с этого момента мы устанавливаем @id
себе (игнорируя любые идентификаторы в таблице).
Он проходит через таблицу только один раз, но должен проходить через всю таблицу, даже если выбранное значение является первой записью. Поскольку средняя позиция составляет половину таблицы (и меньше, если она упорядочена по возрастанию веса), написание цикла может быть быстрее ... (особенно если весовые коэффициенты в общих группах):
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
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), 0)
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