Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Случаен претеглен избор в T-SQL

Отговорът на Дейн включва самосъединяване по начин, който въвежда квадратен закон. (n*n/2) редове след съединението, където има n реда в таблицата.

Това, което би било по-идеално, е да можете да анализирате таблицата само веднъж.

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 към себе си (пренебрегвайки всички идентификатори в таблицата).

Това преминава през таблицата само веднъж, но трябва да премине през цялата таблица, дори ако избраната стойност е първият запис. Тъй като средната позиция е по средата на таблицата (и по-малко, ако е подредена по възходящо тегло), писането на цикъл може да бъде по-бързо... (Особено ако претеглянията са в общи групи):

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Изключение 0x80040154, генерирано по време на изпълнение на прост ssis пакет в MS SQL 2008R2 среда

  2. Файловете FILESTREAM остават след изтриването на реда

  3. Как да използвате Top with Ties в SQL Server - SQL Server / TSQL урок, част 114

  4. вземете датата на всички съботи в дадена година - sql сървър

  5. Sql заявка с присъединяване и групиране по и