Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Losowy wybór ważony w T-SQL

Odpowiedź Dane'a zawiera sprzężenia self w sposób, który wprowadza prawo kwadratowe. (n*n/2) wierszy po złączeniu, gdzie w tabeli jest n wierszy.

Bardziej idealne byłoby jednokrotne przeanalizowanie tabeli.

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

To przejdzie przez tabelę, ustawiając @id do id każdego rekordu wartość, jednocześnie zmniejszając @weight punkt. Ostatecznie @weight_point pójdzie ujemnie. Oznacza to, że SUM wszystkich poprzednich wag jest większa niż losowo wybrana wartość docelowa. To jest rekord, którego chcemy, więc od tego momentu ustawiamy @id do siebie (ignorując wszelkie identyfikatory w tabeli).

Przechodzi przez tabelę tylko raz, ale musi przebiegać przez całą tabelę, nawet jeśli wybrana wartość jest pierwszym rekordem. Ponieważ średnia pozycja znajduje się w połowie tabeli (i mniej, jeśli jest uporządkowana według rosnącej wagi), pisanie pętli może być prawdopodobnie szybsze... (Zwłaszcza jeśli wagi są we wspólnych grupach):

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. Wykryj kolejne zakresy dat za pomocą SQL

  2. Przechowywanie dużych plików w bazie danych powoduje wyjątek typu „System.OutOfMemoryException”

  3. Zwróć listę wszystkich wyzwalaczy serwera w SQL Server

  4. C++ wstępnie określa długość VARCHAR przy użyciu parametrów związanych z ODBC

  5. Gdzie są rejestrowane próby połączenia z programem SQL Server?