sql >> Databáze >  >> RDS >> Sqlserver

Náhodná vážená volba v T-SQL

Daneova odpověď zahrnuje vlastní spojení způsobem, který zavádí čtvercový zákon. (n*n/2) řádků po spojení, kde je v tabulce n řádků.

Ideální by bylo mít možnost tabulku analyzovat pouze jednou.

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

Toto projde tabulkou s nastavením @id na id každého záznamu hodnotu při současném snížení @weight směřovat. Nakonec @weight_point bude negativní. To znamená, že SUM všech předchozích vah je větší než náhodně vybraná cílová hodnota. Toto je záznam, který chceme, takže od tohoto okamžiku nastavujeme @id sám sobě (ignoruje všechna ID v tabulce).

To projde tabulkou pouze jednou, ale musí projít celou tabulkou, i když je zvolená hodnota prvním záznamem. Protože průměrná pozice je v polovině tabulky (a méně, pokud je řazena vzestupnou váhou), psaní smyčky by mohlo být rychlejší... (Zejména pokud jsou váhy ve společných skupinách):

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. Povolit speciální znaky SQL Server 2008

  2. Analýza I/O výkonu pro SQL Server

  3. Rozdíl mezi 'AND' a '&&' v SQL

  4. Sloupec přejmenování MySQL