sql >> Database >  >> RDS >> Sqlserver

Willekeurige gewogen keuze in T-SQL

Dane's antwoord omvat een self joins op een manier die een kwadratische wet introduceert. (n*n/2) rijen na de join waar er n rijen in de tabel zijn.

Wat ideaal zou zijn, is om de tabel maar één keer te kunnen ontleden.

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

Dit gaat door de tabel, instelling @id naar de id van elk record waarde terwijl tegelijkertijd @weight wordt verlaagd punt. Uiteindelijk zal de @weight_point negatief zal gaan. Dit betekent dat de SUM van alle voorgaande gewichten groter is dan de willekeurig gekozen streefwaarde. Dit is het record dat we willen, dus vanaf dat moment stellen we @id . in naar zichzelf (waarbij ID's in de tabel worden genegeerd).

Deze loopt maar één keer door de tabel, maar moet wel de hele tabel doorlopen, ook als de gekozen waarde het eerste record is. Omdat de gemiddelde positie halverwege de tabel is (en minder indien gerangschikt op oplopend gewicht), kan het schrijven van een lus mogelijk sneller zijn... (Vooral als de wegingen in gemeenschappelijke groepen zijn):

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. Een kolom uitsluiten met SELECT * [behalve columnA] FROM tableA?

  2. Veel-op-veel op dezelfde tafel

  3. Postgresql-gegevenscluster verplaatsen

  4. Hoe maak je n-gram in postgresql