Estou tentando calcular o total em execução. Mas ele deve redefinir quando a soma cumulativa maior que outro valor da coluna
create table #reset_runn_total
(
id int identity(1,1),
val int,
reset_val int,
grp int
)
insert into #reset_runn_total
values
(1,10,1),
(8,12,1),(6,14,1),(5,10,1),(6,13,1),(3,11,1),(9,8,1),(10,12,1)
SELECT Row_number()OVER(partition BY grp ORDER BY id)AS rn,*
INTO #test
FROM #reset_runn_total
Detalhes do índice:
CREATE UNIQUE CLUSTERED INDEX ix_load_reset_runn_total
ON #test(rn, grp)
dados de amostra
+----+-----+-----------+-----+
| id | val | reset_val | Grp |
+----+-----+-----------+-----+
| 1 | 1 | 10 | 1 |
| 2 | 8 | 12 | 1 |
| 3 | 6 | 14 | 1 |
| 4 | 5 | 10 | 1 |
| 5 | 6 | 13 | 1 |
| 6 | 3 | 11 | 1 |
| 7 | 9 | 8 | 1 |
| 8 | 10 | 12 | 1 |
+----+-----+-----------+-----+
Resultado esperado
+----+-----+-----------------+-------------+
| id | val | reset_val | Running_tot |
+----+-----+-----------------+-------------+
| 1 | 1 | 10 | 1 |
| 2 | 8 | 12 | 9 | --1+8
| 3 | 6 | 14 | 15 | --1+8+6 -- greater than reset val
| 4 | 5 | 10 | 5 | --reset
| 5 | 6 | 13 | 11 | --5+6
| 6 | 3 | 11 | 14 | --5+6+3 -- greater than reset val
| 7 | 9 | 8 | 9 | --reset -- greater than reset val
| 8 | 10 | 12 | 10 | --reset
+----+-----+-----------------+-------------+
Inquerir:
Eu obtive o resultado usando Recursive CTE
. A pergunta original está aqui /programming/42085404/reset-running-total-based-on-another-column
;WITH cte
AS (SELECT rn,id,
val,
reset_val,
grp,
val AS running_total,
Iif (val > reset_val, 1, 0) AS flag
FROM #test
WHERE rn = 1
UNION ALL
SELECT r.*,
Iif(c.flag = 1, r.val, c.running_total + r.val),
Iif(Iif(c.flag = 1, r.val, c.running_total + r.val) > r.reset_val, 1, 0)
FROM cte c
JOIN #test r
ON r.grp = c.grp
AND r.rn = c.rn + 1)
SELECT *
FROM cte
Existe alguma alternativa melhor T-SQL
sem usar CLR
?
50000
grupos com60
identidades . portanto, a contagem total de registros estará disponível3000000
. Tenho certeza deRecursive CTE
que não será bem dimensionado3000000
. Atualizará as métricas quando eu voltar ao escritório. Podemos conseguir isso usandosum()Over(Order by)
como você usou neste artigo sqlperformance.com/2012/07/t-sql-queries/running-totalsRespostas:
Eu olhei para problemas semelhantes e nunca consegui encontrar uma solução de função de janela que faça uma única passagem sobre os dados. Eu não acho que é possível. As funções da janela precisam poder ser aplicadas a todos os valores em uma coluna. Isso dificulta cálculos de redefinição como esse, porque uma redefinição altera o valor de todos os seguintes valores.
Uma maneira de pensar sobre o problema é que você pode obter o resultado final desejado se calcular um total de execução básico desde que possa subtrair o total de execução da linha anterior correta. Por exemplo, em seus dados de amostra, o valor para
id
4 é orunning total of row 4 - the running total of row 3
. O valor paraid
6 é orunning total of row 6 - the running total of row 3
porque uma redefinição ainda não aconteceu. O valor paraid
7 é orunning total of row 7 - the running total of row 6
e assim por diante.Eu abordaria isso com o T-SQL em um loop. Fiquei um pouco empolgado e acho que tenho uma solução completa. Para 3 milhões de linhas e 500 grupos, o código terminou em 24 segundos na minha área de trabalho. Estou testando com o SQL Server 2016 Developer edition com 6 vCPU. Estou aproveitando as inserções paralelas e a execução paralela em geral, portanto, talvez você precise alterar o código se estiver em uma versão mais antiga ou tiver limitações de DOP.
Abaixo do código que eu usei para gerar os dados. Os intervalos
VAL
eRESET_VAL
devem ser semelhantes aos seus dados de amostra.O algoritmo é o seguinte:
1) Comece inserindo todas as linhas com um total em execução padrão em uma tabela temporária.
2) Em um loop:
2a) Para cada grupo, calcule a primeira linha com um total de execução acima do reset_value restante na tabela e armazene o ID, o total de execução que era muito grande e o total de execução anterior que era muito grande em uma tabela temporária.
2b) Exclua linhas da primeira tabela temporária em uma tabela temporária de resultados que tenha um valor
ID
menor ou igual aoID
da segunda tabela temporária. Use as outras colunas para ajustar o total atual, conforme necessário.3) Após a exclusão não processar mais as linhas, execute um procedimento adicional
DELETE OUTPUT
na tabela de resultados. Isso é para linhas no final do grupo que nunca excedem o valor de redefinição.Vou passar por uma implementação do algoritmo acima no T-SQL passo a passo.
Comece criando algumas tabelas temporárias.
#initial_results
mantém os dados originais com o total de execução padrão,#group_bookkeeping
é atualizado a cada loop para descobrir quais linhas podem ser movidas e#final_results
contém os resultados com o total de execução ajustado para redefinições.Eu crio o índice em cluster na tabela temp depois para que a inserção e a compilação do índice possam ser feitas em paralelo. Fez uma grande diferença na minha máquina, mas talvez não na sua. Criar um índice na tabela de origem não pareceu ajudar, mas isso poderia ajudar na sua máquina.
O código abaixo é executado no loop e atualiza a tabela de contabilidade. Para cada grupo, precisamos encontrar o máximo
ID
que deve ser movido para a tabela de resultados. Precisamos do total atual dessa linha para que possamos subtraí-lo do total inicial. Agrp_done
coluna é definida como 1 quando não há mais trabalho a ser feito para agrp
.Realmente não
LOOP JOIN
sou fã da dica em geral, mas essa é uma consulta simples e foi a maneira mais rápida de obter o que eu queria. Para otimizar o tempo de resposta, eu queria junções de loop aninhadas paralelas em vez de junções de mesclagem DOP 1.O código abaixo é executado no loop e move os dados da tabela inicial para a tabela de resultados finais. Observe o ajuste no total inicial em execução.
Para sua conveniência, abaixo está o código completo:
fonte
Recursive CTE
demorou 2 minutos e 15 segundosUsando um CURSOR:
Confira aqui: http://rextester.com/WSPLO95303
fonte
Não com janela, mas com a versão SQL pura:
Não sou especialista em dialeto do SQL Server. Esta é uma versão inicial do PostrgreSQL (se bem entendi, não posso usar o LIMIT 1 / TOP 1 na parte recursiva do SQL Server):
fonte
grp
coluna.Parece que você tem várias consultas / métodos para atacar o problema, mas não nos forneceu - ou sequer considerou? - os índices na mesa.
Quais índices existem na tabela? É um heap ou possui um índice em cluster?
Eu tentaria as várias soluções sugeridas após adicionar este índice:
Ou apenas altere (ou crie) o índice clusterizado
(grp, id)
.Ter um índice direcionado à consulta específica deve melhorar a eficiência - da maioria, se não de todos os métodos.
fonte