sql >> Database teknologi >  >> RDS >> Sqlserver

Tilfældigt vægtet valg i T-SQL

Danskerens svar omfatter en selv-sammenføjning på en måde, der indfører en firkantet lov. (n*n/2) rækker efter sammenføjningen, hvor der er n rækker i tabellen.

Hvad der ville være mere ideelt er at kunne parse tabellen én gang.

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

Dette vil gå gennem tabellen og indstille @id til hver posts id værdi, mens @weight på samme tid dekrementeres punkt. Til sidst, @weight_point vil gå negativt. Det betyder, at SUM af alle foregående vægte er større end den tilfældigt valgte målværdi. Dette er den post, vi ønsker, så fra det tidspunkt og fremefter sætter vi @id til sig selv (ignorerer eventuelle ID'er i tabellen).

Dette løber gennem tabellen kun én gang, men skal køre gennem hele tabellen, selvom den valgte værdi er den første post. Fordi den gennemsnitlige position er halvvejs gennem tabellen (og mindre, hvis den sorteres efter stigende vægt), kunne det muligvis være hurtigere at skrive en loop... (Især hvis vægtningerne er i fælles grupper):

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. Flere docker-containere til mysql eller én instans med flere databaser

  2. SQLSTATE[HY000] [1045] Adgang nægtet for brugeren 'brugernavn'@'localhost' ved hjælp af CakePHP

  3. PostgreSql kommando for at se tabeldataene

  4. billedtypen er ikke indstillet til polymorfe associationer