Imagine a seguinte tabela (chamada TestTable
):
id somedate somevalue
-- -------- ---------
45 01/Jan/09 3
23 08/Jan/09 5
12 02/Feb/09 0
77 14/Feb/09 7
39 20/Feb/09 34
33 02/Mar/09 6
Gostaria de uma consulta que retorne um total em execução na ordem da data, como:
id somedate somevalue runningtotal
-- -------- --------- ------------
45 01/Jan/09 3 3
23 08/Jan/09 5 8
12 02/Feb/09 0 8
77 14/Feb/09 7 15
39 20/Feb/09 34 49
33 02/Mar/09 6 55
Eu sei que existem várias maneiras de fazer isso no SQL Server 2000/2005/2008.
Estou particularmente interessado nesse tipo de método que usa o truque de agregação de conjunto de instruções:
INSERT INTO @AnotherTbl(id, somedate, somevalue, runningtotal)
SELECT id, somedate, somevalue, null
FROM TestTable
ORDER BY somedate
DECLARE @RunningTotal int
SET @RunningTotal = 0
UPDATE @AnotherTbl
SET @RunningTotal = runningtotal = @RunningTotal + somevalue
FROM @AnotherTbl
... isso é muito eficiente, mas ouvi dizer que há problemas em torno disso, porque você não pode necessariamente garantir que a UPDATE
instrução processe as linhas na ordem correta. Talvez possamos obter algumas respostas definitivas sobre esse problema.
Mas talvez haja outras maneiras que as pessoas possam sugerir?
edit: Agora com um SqlFiddle com a instalação e o exemplo 'truque de atualização' acima
sql
sql-server
tsql
running-total
codeulike
fonte
fonte
Respostas:
Atualização , se você estiver executando o SQL Server 2012, consulte: https://stackoverflow.com/a/10309947
O problema é que a implementação do SQL Server da cláusula Over é um pouco limitada .
Oracle (e ANSI-SQL) permitem que você faça coisas como:
O SQL Server não fornece uma solução limpa para esse problema. Meu instinto está me dizendo que esse é um daqueles casos raros em que um cursor é o mais rápido, embora eu tenha que fazer alguns testes comparativos de grandes resultados.
O truque de atualização é útil, mas eu sinto que é bastante frágil. Parece que se você estiver atualizando uma tabela completa, ela prosseguirá na ordem da chave primária. Portanto, se você definir sua data como uma chave primária ascendente,
probably
estará seguro. Mas você está confiando em um detalhe de implementação não documentado do SQL Server (também se a consulta acabar sendo executada por dois procs, imagino o que acontecerá, consulte: MAXDOP):Amostra de trabalho completa:
Você pediu um benchmark, este é o ponto inicial.
A maneira mais rápida e segura de fazer isso seria o Cursor, é uma ordem de magnitude mais rápida que a subconsulta correlacionada da junção cruzada.
O caminho mais rápido é o truque UPDATE. Minha única preocupação é que não tenho certeza de que, em todas as circunstâncias, a atualização ocorrerá de maneira linear. Não há nada na consulta que diga isso explicitamente.
Bottom line, para o código de produção eu iria com o cursor.
Dados de teste:
Teste 1:
Teste 2:
Teste 3:
Teste 4:
fonte
No SQL Server 2012, você pode usar SUM () com a cláusula OVER () .
SQL Fiddle
fonte
Embora Sam Saffron tenha feito um ótimo trabalho, ele ainda não forneceu código de expressão de tabela comum recursivo para esse problema. E para nós que trabalhamos com o SQL Server 2008 R2 e não com o Denali, ainda é a maneira mais rápida de executar o total, é cerca de 10 vezes mais rápido que o cursor no meu computador de trabalho por 100000 linhas, e também é uma consulta embutida.
Então, aqui está (suponho que exista uma
ord
coluna na tabela e seu número seqüencial sem lacunas, para um processamento rápido, também deve haver uma restrição exclusiva nesse número):sql fiddle demo
update Eu também estava curioso sobre esta atualização com atualização variável ou peculiar . Então geralmente funciona bem, mas como podemos ter certeza de que funciona sempre? bem, aqui está um pequeno truque (encontrado aqui - http://www.sqlservercentral.com/Forums/Topic802558-203-21.aspx#bm981258 ) - basta verificar a corrente e a anterior
ord
e usar a1/0
atribuição caso sejam diferentes do que você está esperando:Pelo que vi, se você possui um índice / chave primária em cluster adequado em sua tabela (no nosso caso, seria um índice por
ord_id
), a atualização continuará de maneira linear o tempo todo (nunca foi encontrado dividir por zero). Dito isto, você decide se deseja usá-lo no código de produção :)atualização 2 Estou vinculando esta resposta, pois inclui algumas informações úteis sobre a confiabilidade da atualização peculiar - comportamento inexplicável da concatenação nvarchar / índice / nvarchar (max) .
fonte
O operador APPLY no SQL 2005 e superior trabalha para isso:
fonte
Você também pode usar a função ROW_NUMBER () e uma tabela temporária para criar uma coluna arbitrária para usar na comparação na instrução SELECT interna.
fonte
Use uma subconsulta correlacionada. Muito simples, aqui está:
O código pode não estar exatamente correto, mas tenho certeza de que a ideia é.
O GROUP BY é caso uma data apareça mais de uma vez, você só quer vê-la uma vez no conjunto de resultados.
Se você não se importa em ver datas repetidas ou deseja ver o valor e o ID originais, deseja o seguinte:
fonte
Você também pode desnormalizar - armazene os totais em execução na mesma tabela:
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx
Seleciona trabalho muito mais rápido do que qualquer outra solução, mas as modificações podem ser mais lentas
fonte
Supondo que a janela funcione no SQL Server 2008 como em outros lugares (que eu tentei), experimente:
O MSDN diz que está disponível no SQL Server 2008 (e talvez também em 2005?), Mas não tenho uma instância disponível para testá-lo.
EDIT: bem, aparentemente o SQL Server não permite uma especificação de janela ("OVER (...)") sem especificar "PARTITION BY" (dividindo o resultado em grupos, mas não agregando da mesma maneira que o GROUP BY). Irritante - a referência de sintaxe do MSDN sugere que isso é opcional, mas eu só tenho instâncias do SqlServer 2000 no momento.
A consulta que forneci funciona no Oracle 10.2.0.3.0 e no PostgreSQL 8.4-beta. Então diga à MS para recuperar o atraso;)
fonte
1 partitionme
e particione com isso. Além disso, a partição por é provavelmente necessária em situações da vida real ao fazer relatórios.Se você estiver usando o Sql server 2008 R2 acima. Então, seria a maneira mais curta de fazer;
LAG é usado para obter o valor da linha anterior. Você pode fazer o google para mais informações.
[1]:
fonte
SUM(somevalue) OVER(...)
o que parece um aspirador muito para mimAcredito que um total em execução possa ser alcançado usando a operação simples INNER JOIN abaixo.
fonte
O seguinte produzirá os resultados necessários.
Ter um índice em cluster no SomeDate melhorará bastante o desempenho.
fonte
Usando junção Outra variação é usar junção. Agora a consulta pode parecer com:
para mais informações, visite este link http://askme.indianyouth.info/details/calculating-simple-running-totals-in-sql-server-12
fonte
Embora a melhor maneira de fazer isso seja usar uma função de janela, isso também pode ser feito usando uma simples consulta secundária correlacionada .
fonte
fonte
Aqui estão duas maneiras simples de calcular o total em execução:
Abordagem 1 : pode ser escrito desta maneira se o seu DBMS suportar funções analíticas
Abordagem 2 : você pode usar OUTER APPLY se a versão do banco de dados / DBMS em si não suportar funções analíticas
Nota: - Se você precisar calcular o total de execução para diferentes partições separadamente, isso pode ser feito conforme publicado aqui: Calculando totais de execução em linhas e agrupando por ID
fonte