Câu trả lời của Dane bao gồm một tự tham gia theo cách đưa ra luật bình phương. (n*n/2)
các hàng sau phép nối trong đó có n hàng trong bảng.
Điều lý tưởng hơn là có thể chỉ phân tích cú pháp bảng một lầ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))
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
Điều này sẽ đi qua bảng, thiết lập @id
đến id
của mỗi bản ghi giá trị đồng thời giảm @weight
điểm. Cuối cùng, @weight_point
sẽ chuyển sang tiêu cực. Điều này có nghĩa là SUM
của tất cả các trọng số trước đó đều lớn hơn giá trị mục tiêu được chọn ngẫu nhiên. Đây là bản ghi chúng tôi muốn, vì vậy từ thời điểm đó trở đi, chúng tôi đặt @id
cho chính nó (bỏ qua bất kỳ ID nào trong bảng).
Thao tác này chỉ chạy qua bảng một lần, nhưng phải chạy qua toàn bộ bảng ngay cả khi giá trị được chọn là bản ghi đầu tiên. Bởi vì vị trí trung bình là một nửa của bảng (và ít hơn nếu được sắp xếp theo trọng số tăng dần) nên việc viết một vòng lặp có thể nhanh hơn ... (Đặc biệt nếu các trọng số nằm trong các nhóm chung):
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