Я хотел бы выделить в 4 группы данные из таблицы, имеющей сумму значений в группах, как можно более равномерно распределенных. Я уверен, что я не объясняю это достаточно ясно, поэтому я попытаюсь привести пример.
Здесь я использую NTILE (4) для создания 4 групп:
SELECT Time, NTILE(4) OVER (ORDER BY Time DESC) AS N FROM TableX
Time - N
-------------
10 - 1
9 - 2
8 - 3
7 - 4
6 - 1
5 - 2
4 - 3
3 - 4
2 - 1
1 - 2
В приведенном выше запросе и результате другие столбцы были опущены для краткости.
Таким образом, вы можете увидеть группы также следующим образом:
1 2 3 4
--- --- --- ---
10 9 8 7
6 5 4 3
2 1
--- --- --- ---
18 15 12 10 Sum Totals of Time
Обратите внимание, что сумма итогов времени с использованием NTile не очень сбалансирована между группами. Лучшее распределение значений времени будет, например:
1 2 3 4
--- --- --- ---
10 9 8 7
3 5 4 6
1 2
--- --- --- ---
14 14 14 13 Sum Totals of Time
Здесь сумма итогов времени более равномерно распределена по 4 группам.
Как я могу выполнить это через операторы TSQL?
Кроме того, я должен сказать, что я использую SQL Server 2012. Если у вас есть что-то, что может мне помочь, дайте мне знать.
Хорошего дня.
Стан
Ответы:
Вот удар по алгоритму. Он не идеален, и в зависимости от того, сколько времени вы хотите потратить на его переработку, возможно, будут достигнуты еще некоторые небольшие выгоды.
Предположим, у вас есть таблица задач, которые должны быть выполнены четырьмя очередями. Вы знаете объем работы, связанной с выполнением каждой задачи, и хотите, чтобы все четыре очереди выполняли почти одинаковый объем работы, поэтому все очереди будут завершены примерно в одно и то же время.
Прежде всего, я бы разделил задачи, используя модульную, упорядоченную по размеру, от малого до большого.
Эти
ROW_NUMBER()
заказы каждый ряд по размеру, а затем назначает номер строки, начиная с 1. Этот номер строки присваивается «группа» (Thegrp
столбца) на круговом основе. Первый ряд - это группа 1, второй ряд - это группа 2, затем 3, четвертый - группа 0 и т. Д.Для простоты использования, я храню
time
иgrp
столбцов в табличной переменной называется@work
.Теперь мы можем выполнить несколько расчетов по этим данным:
Колонка
_grpoffset
сколько общееtime
вgrp
отличается от «идеального» среднего. Если общееtime
количество всех заданий равно 1000 и имеется четыре группы, в идеале должно быть в общей сложности 250 в каждой группе. Если группа содержит в общей сложности 268, эта группа_grpoffset=18
.Идея состоит в том, чтобы определить две лучшие строки: одну в «положительной» группе (с большим количеством работы) и одну в «отрицательной» группе (с небольшим количеством работы). Если бы мы могли поменять местами группы в этих двух строках, мы могли бы уменьшить абсолютное значение
_grpoffset
для обеих групп.Пример:
При общей сумме в 727 баллов каждая группа должна набрать около 182 баллов, чтобы распределение было идеальным. Разница между оценкой группы и 182 - это то, что мы помещаем в
_grpoffset
колонку.Как вы можете видеть сейчас, в лучшем из миров мы должны переместить ряды примерно на 40 баллов из группы 1 в группу 2 и около 24 баллов из группы 3 в группу 0.
Вот код для идентификации этих строк-кандидатов:
Я сам присоединяюсь к общему табличному выражению, которое мы создали ранее
cte
: с одной стороны, группы с положительными_grpoffset
, с другой стороны - с отрицательными. Чтобы дополнительно отфильтровать, какие строки должны соответствовать друг другу, своп положительных и отрицательных сторон должен улучшиться_grpoffset
, то есть приблизиться к 0.Параметр
TOP 1
иORDER BY
выбирает «лучшее» совпадение для замены в первую очередь.Теперь все, что нам нужно, это добавить
UPDATE
и зациклить его, пока не будет больше никакой оптимизации.TL; DR - вот запрос
Вот полный код:
источник