sql >> Databasteknik >  >> RDS >> Sqlserver

Slumpmässigt vägt val i T-SQL

Danes svar inkluderar ett jag går med på ett sätt som introducerar en kvadratisk lag. (n*n/2) rader efter sammanfogningen där det finns n rader i tabellen.

Vad som skulle vara mer idealiskt är att bara kunna analysera tabellen en gång.

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

Detta går igenom tabellen och ställer in @id till varje posts id värde samtidigt som @weight minskar punkt. Så småningom, @weight_point kommer att bli negativ. Det betyder att SUM av alla föregående vikter är större än det slumpmässigt valda målvärdet. Det här är posten vi vill ha, så från den punkten och framåt ställer vi in ​​@id till sig själv (ignorerar eventuella ID:n i tabellen).

Detta går igenom tabellen bara en gång, men måste gå igenom hela tabellen även om det valda värdet är den första posten. Eftersom den genomsnittliga positionen är halvvägs genom tabellen (och mindre om den sorteras efter stigande vikt) kan det möjligen gå snabbare att skriva en loop... (Speciellt om viktningarna är i gemensamma 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. hur man skapar en lagrad procedur i Oracle som accepterar en rad parametrar

  2. Vill du ställa in fältet för att automatiskt infoga tidsstämpel vid UPPDATERING?

  3. CodeIgniter infogar två gånger samma post. I vissa fall

  4. php pdo emulate_prepares false stringify_fetches false int still string