Eu gostaria de selecionar em 4 grupos os dados de uma tabela com a soma de valores nos grupos da forma mais distribuída possível. Tenho certeza de que não estou explicando isso com clareza o suficiente, então tentarei dar um exemplo.
Aqui eu uso o NTILE (4) para criar os 4 grupos:
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
Na consulta e resultado acima, as outras colunas foram omitidas por questões de brevidade.
Então você pode ver os grupos também da seguinte maneira:
1 2 3 4
--- --- --- ---
10 9 8 7
6 5 4 3
2 1
--- --- --- ---
18 15 12 10 Sum Totals of Time
Observe que a soma total de tempo usando o NTile não é realmente equilibrada entre os grupos. Uma melhor distribuição dos valores de tempo seria, por exemplo:
1 2 3 4
--- --- --- ---
10 9 8 7
3 5 4 6
1 2
--- --- --- ---
14 14 14 13 Sum Totals of Time
Aqui, a soma dos totais de tempo é distribuída de maneira mais uniforme nos 4 grupos.
Como posso fazer isso através de uma instrução TSQL?
Além disso, devo dizer que estou usando o SQL Server 2012. Se você tiver algo que possa me ajudar, me avise.
Eu te desejo um bom dia.
Stan
fonte
Respostas:
Aqui está uma facada em um algoritmo. Não é perfeito e, dependendo de quanto tempo você deseja gastar para refiná-lo, provavelmente há outros pequenos ganhos a serem feitos.
Vamos supor que você tenha uma tabela de tarefas a serem executadas por quatro filas. Você sabe a quantidade de trabalho associada à execução de cada tarefa e deseja que todas as quatro filas tenham uma quantidade quase igual de trabalho, para que todas as filas sejam concluídas aproximadamente ao mesmo tempo.
Primeiro, particionaria as tarefas usando um modulado, ordenado por seu tamanho, de pequeno a grande porte.
As
ROW_NUMBER()
ordens de cada linha por tamanho, então atribui um número de linha, a partir de 1. Este número da linha é atribuído um "grupo" (ogrp
coluna) numa base round-robin. A primeira linha é o grupo 1, a segunda linha é o grupo 2, depois a 3, a quarta recebe o grupo 0 e assim por diante.Para facilitar o uso, estou armazenando as colunas
time
egrp
em uma variável de tabela chamada@work
.Agora, podemos realizar alguns cálculos nesses dados:
A coluna
_grpoffset
é quanto o totaltime
porgrp
difere da média "ideal". Se o totaltime
de todas as tarefas for 1000 e houver quatro grupos, idealmente deve haver um total de 250 em cada grupo. Se um grupo contiver um total de 268, esse grupo será_grpoffset=18
.A idéia é identificar as duas melhores linhas, uma em um grupo "positivo" (com muito trabalho) e outra em um grupo "negativo" (com muito pouco trabalho). Se pudermos trocar grupos nessas duas linhas, poderemos reduzir o absoluto
_grpoffset
de ambos os grupos.Exemplo:
Com um total geral de 727, cada grupo deve ter uma pontuação de cerca de 182 para que a distribuição seja perfeita. A diferença entre a pontuação do grupo e 182 é o que estamos colocando na
_grpoffset
coluna.Como você pode ver agora, no melhor dos mundos, devemos mover cerca de 40 pontos no valor de linhas do grupo 1 para o grupo 2 e cerca de 24 pontos do grupo 3 para o grupo 0.
Aqui está o código para identificar essas linhas candidatas:
Estou me unindo à expressão de tabela comum que criamos antes
cte
: Por um lado, grupos com um positivo e_grpoffset
, por outro, grupos com negativos. Para filtrar ainda mais quais linhas devem coincidir entre si, a troca das linhas dos lados positivo e negativo deve melhorar_grpoffset
, ou seja, aproximar-se de 0.O
TOP 1
eORDER BY
seleciona a "melhor" correspondência para trocar primeiro.Agora, basta adicionar um
UPDATE
e fazer um loop até que não haja mais otimização a ser encontrada.TL; DR - aqui está a consulta
Aqui está o código completo:
fonte