sql >> データベース >  >> RDS >> Sqlserver

T-SQLでのランダム加重選択

    デーンの答えには、二乗法則を導入する方法での自己結合が含まれています。 (n * n / 2) テーブルにn行ある結合後の行。

    より理想的なのは、テーブルを1回だけ解析できるようにすることです。

    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
    

    これはテーブルを通過し、 @idを設定します 各レコードのidに 同時に@weightをデクリメントしながら値 点。最終的に、 @weight_point ネガティブになります。これは、 SUMが 先行するすべての重みのうち、ランダムに選択されたターゲット値よりも大きくなります。これは必要なレコードなので、それ以降は @idを設定します。 それ自体に(テーブル内のIDを無視します)。

    これはテーブルを1回だけ実行しますが、選択した値が最初のレコードであっても、テーブル全体を実行する必要があります。平均位置はテーブルの途中にあるため(重みの昇順で並べ替えた場合はそれより少なくなります)、ループの記述が速くなる可能性があります...(特に重みが共通のグループにある場合):

    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. DeprecationWarning:ブール値がoptions.operatorsAliasesに渡されました。これはv5での操作ではないため、削除する必要があります

    2. PostgreSQLのカーソルベースのレコード

    3. Ora-01427の単一行サブクエリがselectで複数の行を返す問題を修正するにはどうすればよいですか?

    4. Ubuntu 9.04(Jaunty)でMySQLリレーショナルデータベースを使用する