Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
Hi all, good evening,
I need to compare the cumulative sales between a year and the year before. This for many years stored in my database.
The sales had already been normalized by the month of each year.
This is the expression I created to generate the information for each year:
Aggr(RangeSum(Above(Sum(QTY), 0, RowNo())), [SALES YEAR, [YEAR/MONTH])
| YEAR/MONTH | SALES YEAR | QTY |
| (other year months) | ... | ... |
| 2026/01 | 2024 | 1 |
| 2026/02 | 2024 | 10 |
| 2026/03 | 2024 | 25 |
| 2026/04 | 2024 | 8 |
| 2026/05 | 2024 | 14 |
| ... | ... | ... |
| 2026/01 | 2025 | 0 |
| 2026/02 | 2025 | 3 |
| 2026/03 | 2025 | 12 |
| 2026/04 | 2025 | 11 |
| 2026/05 | 2025 | 7 |
| ... | ... | ... |
| 2026/01 | 2026 | 4 |
| 2026/02 | 2026 | 5 |
| 2026/03 | 2026 | 8 |
| 2026/04 | 2026 | 12 |
| 2026/05 | 2026 | 20 |
And this is the generated graphic:
What I need is to created a new graphic that shows, for each year, the % growth between that year vs its last year, using the same rule (acumulated sales for each month).
I have already tried many solutions here in the topics, but I couldn't get the right result.
For exemple: Aggr(RangeSum(Above(Sum(QTY) / Sum({<[SALES YEAR] = {"$(=Only([SALES YEAR])-1)"}>}QTY), 0, RowNo())), [SALES YEAR, [YEAR/MONTH])
Thanks in advance for any help.
try this way?
((Aggr(Max(RangeSum(Above(Sum(QTY),0,RowNo()))),[SALES YEAR]) - Above(Aggr(Max(RangeSum(Above(Sum(QTY),0,RowNo()))),[SALES YEAR]),1)) / Above(Aggr(Max(RangeSum(Above(Sum(QTY),0,RowNo()))),[SALES YEAR]),1)) * 100
Hi @Anil_Babu_Samineni ,
I tried your solution but it didn't work.
I will try to send more details about my data set, to try to clarify better the issue.
I will send this next monday.
Thanks in advance for your help!
I'd prefer/suggest to the calculate in the script rather than on the UI as it becomes complicated on the front end especially for cumulative aggregations using rangesum().
Ótimo caso, no fundo é um YoY (crescimento ano contra ano) em cima de um valor acumulado. Vou por partes.
Sua expressão de acumulado está correta
Aggr(
RangeSum(Above(Sum(Quantidade), 0, RowNo())),
ANO_VENDAS, ANO_MÊS
)
Ela reinicia o acumulado a cada `ANO_VENDAS` (porque a acumulação acontece dentro de cada grupo do `Aggr`) e vai somando mês a mês na ordem de `ANO_MÊS`. Um detalhe que decorre disso e é importante para o próximo passo: o acumulado no último mês de um ano é igual ao total daquele ano.
Que gráfico representa melhor?
Depende da granularidade que você quer enxergar:
Se você quer um número de crescimento por ano (ex.: 2024 cresceu +12% sobre 2023), use gráfico de colunas/barras. É o melhor porque o crescimento pode ser negativo, e a coluna acima/abaixo de zero comunica isso na hora. Ano no eixo X.
Se você quer acompanhar o crescimento *acumulado mês a mês*, comparando cada mês com o mesmo mês do ano anterior, use **gráfico de linhas**, com o número do mês (1–12) no eixo X e uma linha por ano. Aí você vê em que ponto do ano o crescimento acelera ou perde força — e resolve o problema de ano incompleto (compara sempre "até o mesmo mês").
Para responder direto à sua pergunta ("crescimento de cada ano vs. o anterior"), vá de colunas.
Expressão — crescimento anual (colunas)
Como o acumulado de dezembro = total do ano, o crescimento anual sai direto, sem precisar reconstruir a acumulação:
Dimensão: ANO_VENDAS (ordem **crescente**)
Medida: Sum(Quantidade) / Above(Sum(Quantidade)) - 1
Formate como %. O `Above()` pega o total da linha de cima (ano anterior). No primeiro ano ele fica nulo — que é o correto, já que não há ano anterior. Só garanta a ordenação crescente da dimensão, senão o `Above()` compara com o ano errado.
Expressão — crescimento acumulado mês a mês (linhas)
Aqui a comparação é "acumulado até o mês X deste ano" vs. "acumulado até o mês X do ano anterior". Fazer isso 100% na expressão (acumulação inter-registro + deslocamento de 12 meses) fica frágil, porque depende de todos os meses existirem em todos os anos. O caminho robusto e rápido é **acumular no script** e deixar o gráfico simples:
Ordenada:
LOAD ANO_VENDAS, ANO_MÊS, Quantidade
RESIDENT SuaTabela
ORDER BY ANO_VENDAS, ANO_MÊS;
Acumulada:
LOAD *,
If(ANO_VENDAS = Peek(ANO_VENDAS),
RangeSum(Peek(Qtd_Acum), Quantidade),
Quantidade) AS Qtd_Acum
RESIDENT Ordenada
ORDER BY ANO_VENDAS, ANO_MÊS;
DROP TABLE Ordenada;
(Se `ANO_MÊS` não ordenar certo como texto — tipo "Jan", "Fev" — crie antes um `MesNum` numérico e use ele no `ORDER BY`.)
Com o campo `Qtd_Acum` pronto, no gráfico de linhas:
Dimensão: `MesNum` (1–12)
Medida (último ano vs. ano anterior):
Sum({<ANO_VENDAS={"$(=Max(ANO_VENDAS))"}>} Qtd_Acum)
/
Sum({<ANO_VENDAS={"$(=Max(ANO_VENDAS)-1)"}>} Qtd_Acum)
- 1
Para comparar *cada* ano com o seu antecessor (não só o último), coloque `ANO_VENDAS` como segunda dimensão (cor) e troque a medida por `Sum(Qtd_Acum)/Above(Total Sum(Qtd_Acum)) - 1`, cuidando da ordem das dimensões.
Resumindo: coluna para o número anual de crescimento; linha para a leitura mês a mês.
Espero ter ajudado.
Thank you for your help, @Qrishna
Olá @WesleySouza , muito obrigado por toda a ajuda e explicações!
Sobre as soluções que você propôs, a minha necessidade seria na direção da sua segunda sugestão, para tratar a % de crescimento mês a mês, acumulada.
Aqui no meu caso, eu garanto que todos os dias e meses do ano existam, mesmo que não tenha ocorrido venda, isso para todos os anos. Acaba que fica mais pesado, mas resolveu o problema. Eu tive que fazer isso para que o meu gráfico que coloquei como exemplo no meu post (mostrando as vendas acumuladas de cada ano), funcionasse sem qualquer problema nas acumulações etc.
Eu citei o exemplo do gráfico com acumulação por ano/mês, mas eu tenho outro gráfico de linha que gera por dia, onde sigo a mesma ideia.
Eu vou postar abaixo os códigos de cada passo que fiz, para que isso fosse possível.
Daí baseado nisso, você acredita que seria possível, diretamente nas expressões do gráfico de linha, chegar no resultado de conseguir comparar as vendas do cada ano, em relação ao ano imediatamente anterior, para cada ano, isso acumulando mês a mês?
Novamente, agradeço muito pela ajuda!
Vendas:
LOAD *;
SQL
-- 1. Cria uma Tabela de Datas com todos os dias do ano
WITH Dates AS (
SELECT CAST('2026-01-01' AS DATE) AS CalendarDate
UNION ALL
SELECT DATEADD(DAY, 1, CalendarDate)
FROM Dates
WHERE CalendarDate < '2028-01-31'
),
-- 2. Identifica todas as combinações únicas de unidade e ano de vendas
UnitYears AS (
SELECT
DISTINCT
UNIDADE AS "Unidade",
ANOVENDA AS "Ano venda",
CODDEPARTAMENTO AS "Departamento",
CODVENDEDOR AS "Vendedor",
FROM
VENDAS (NOLOCK)
),
-- 3. Combina as datas com as unidades e anos para criar a tabela completa de todos os dias por unidade/ano
CompleteMatrix AS (
SELECT
d.CalendarDate,
uy."Unidade",
uy."Ano venda",
uy."Departamento",
uy."Vendedor"
FROM
Dates AS d
CROSS JOIN
UnitYears AS uy
)
-- 4. Junta a tabela completa com a tabela original para preencher os dados
SELECT
m."Unidade",
m."Ano venda",
m."Departamento",
m."Vendedor",
CONVERT(VARCHAR(4),YEAR(m.CalendarDate)) + '/' + SUBSTRING(CONVERT(VARCHAR,m.CalendarDate,111),6,2) AS "Ano/mês",
m.CalendarDate AS data_venda,
ISNULL(t.QTD, 0) AS qtd
FROM
CompleteMatrix AS m
LEFT JOIN
(
SELECT
UNIDADE AS "Unidade",
ANOVENDA AS "Ano venda",
CODDEPARTAMENTO AS "Departamento",
CODVENDEDOR AS "Vendedor",
/* Normaliza as datas de vendas */
CONVERT(NVARCHAR(20),REPLACE(CASE
WHEN YEAR(DTVENDA) = ANOVENDA
THEN CASE
WHEN SUBSTRING(CONVERT(VARCHAR,DTVENDA,111),6,5) = '02/29' THEN CONVERT(VARCHAR(4),YEAR(GETDATE())) + '/' + '02/28'
ELSE CONVERT(VARCHAR(4),YEAR(GETDATE())) + '/' + SUBSTRING(CONVERT(VARCHAR,DTVENDA,111),6,5)
END
END,'/','-')) AS DATA_VENDA,
COUNT(VENDAID) AS QTD
FROM
VENDAS (NOLOCK)
GROUP BY
UNIDADE,
ANOVENDA,
CODDEPARTAMENTO,
CODVENDEDOR,
/* Normaliza as datas de vendas */
CONVERT(NVARCHAR(20),REPLACE(CASE
WHEN YEAR(DTVENDA) = ANOVENDA
THEN CASE
WHEN SUBSTRING(CONVERT(VARCHAR,DTVENDA,111),6,5) = '02/29' THEN CONVERT(VARCHAR(4),YEAR(GETDATE())) + '/' + '02/28'
ELSE CONVERT(VARCHAR(4),YEAR(GETDATE())) + '/' + SUBSTRING(CONVERT(VARCHAR,DTVENDA,111),6,5)
END
END,'/','-'))
) AS T
ON m."Unidade" = t."Unidade"
AND m."Ano venda" = t."Ano venda"
AND m.CalendarDate = t.DATA_VENDA
AND m."Departamento" = t."Departamento"
AND m."Vendedor" = t."Vendedor"
ORDER BY
m.CalendarDate,
m."Ano venda",
m."Unidade"
OPTION (MAXRECURSION 0);
Para ilustrar melhor o objetivo que estou tentando alcançar, o gráfico seria como no exemplo da imagem abaixo. E eu tenho anos de vendas dinâmicos, que são criados a cada novo ano. Então seria comparar a venda acumulada mês a mês do ano com a acumulada mês a mês do ano imediatamente anterior, para todos os anos de venda.
May be you Can take a look at this . I have consider Data for 3 Year 2026,2025,2024
Take a Line Chart
Dimension: Year_Month
Measure :
1.
(RangeSum(Above(Sum(QTY),0,NoOfRows())) - Above(RangeSum(Above(Sum(QTY),0,NoOfRows())))) / Above(RangeSum(Above(Sum(QTY),0,NoOfRows())))
2. RangeSum(Above(Sum(QTY),0,NoOfRows()))
Filter : Select Sales Year
Charts will look like
Bar Charts show actual Sales QTY.
Line/combo charts show
Cumulative SAles Quantity
I have wondering if multiple line you shows in image would be possible.
Thanks
I suggest to simplify the entire approach by removing the year-information from the dimension and just using the month.
Data relating to a certain period are belonging to this and only this period and not to a previous one or reverse. That's not a matter of selections else how the data are associated to each other. I don't want to say that's not possible to bypass these relations with more or less complex aggr-constructs but if the targets are to build on top of it cummulations and/or further rates it could become easily very ugly.
Much simpler would be to remove the differentiating information form the dimension and applying n expressions with appropriate conditions - as base something like:
sum({< Year = {"$(=max(Year))"}>} QTY)
sum({< Year = {"$(=max(Year)-1)"}>} QTY)
...
Another possibility would be to create appropriate associations within the data-model. This means not mandatory to pre-calculate facts else to add an extra and overlapping dimension-layer to it. The logic behind it is explained here: The As-Of Table - Qlik Community - 1466130
Hi @andrelfg
The above and aggr functions can get very complicated when trying to do these things, and will often give unexpected results.
I tend to deal with this type of scenario by creating a separate aggregate dimension in the load script. I explain this approach in this blog post:
https://www.quickintelligence.co.uk/qlikview-accumulate-values/
It's a very similar approach to the one that HIC lays out in the link that @marcus_sommer has posted above, but I look at a few different ways of applying this in my post.
Hope that helps.
Steve