Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Pilihan Tertimbang Acak di T-SQL

Jawaban Dane mencakup gabungan diri dengan cara yang memperkenalkan hukum kuadrat. (n*n/2) baris setelah gabungan di mana ada n baris dalam tabel.

Apa yang lebih ideal adalah hanya dapat mengurai tabel sekali.

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

Ini akan melalui tabel, pengaturan @id ke id setiap record nilai sekaligus mengurangi @weight titik. Akhirnya, @weight_point akan menjadi negatif. Ini berarti bahwa SUM dari semua bobot sebelumnya lebih besar dari nilai target yang dipilih secara acak. Ini adalah rekor yang kami inginkan, jadi sejak saat itu kami menetapkan @id untuk dirinya sendiri (mengabaikan ID apa pun di tabel).

Ini berjalan melalui tabel hanya sekali, tetapi harus berjalan melalui seluruh tabel bahkan jika nilai yang dipilih adalah catatan pertama. Karena posisi rata-rata adalah setengah jalan melalui tabel (dan lebih sedikit jika diurutkan berdasarkan bobot yang meningkat) menulis loop mungkin bisa lebih cepat... (Terutama jika bobotnya berada dalam kelompok yang sama):

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Masalah ASP klasik saat menyambung ke database SQL Server jarak jauh

  2. Akses TimeZoneInfo dari SQL 2005 Server

  3. Menjalankan prosedur tersimpan menggunakan Penjadwal tugas Windows

  4. Gunakan COLUMNPROPERTY() untuk Mengembalikan Kolom atau Informasi Parameter di SQL Server

  5. Saat menjalankan prosedur tersimpan, apa keuntungan menggunakan CommandType.StoredProcedure dibandingkan menggunakan CommandType.Text?