As window functions1 no PostgreSQL facilitam a execução de cálculos em conjuntos de linhas correlacionadas, melhorando a análise de dados, permitindo análises sofisticadas sem a necessidade de agregar os dados. Neste guia, abordaremos desde a criação das tabelas até a aplicação de window functions em cenários reais, incluindo exemplos práticos e explicações detalhadas dos resultados.

Uma Explicação Simples:

Considerando a tabela abaixo, cada linha representa um registro, como uma venda, um cliente ou um produto. As window functions permitem que você realize cálculos sobre um conjunto dessas linhas, não apenas sobre linhas individuais. É como se você tivesse uma “janela” que se move ao longo da sua planilha, realizando cálculos em um grupo de linhas por vez.

#IdUFVendedorClienteProdutoDataValor
1SPvaleria.sacksJoséTeclado10/03/2024120,45
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590,20
3SPvaleria.sacksCamilaSmartphone07/06/20241340,20
4PRroberto.ferreiraEduardoMonitor12/03/2024750,32
5PRroberto.ferreiraMarceloProcessador24/04/2024980,76
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640,55
7RSaline.souzaClaudiaMouse15/03/202485,99
8RSaline.souzaMarianaPlaca Mãe28/04/2024810,45
9RSaline.souzaSimoneMini PC15/06/20241450,99
10SCmarcos.limaLeonardoImpressora18/03/2024654,77
11SCmarcos.limaAndréTeclado02/05/2024135,78
12SCmarcos.limaBrunoMemoria RAM19/06/2024720,33
13RScarla.rodriguesBeatrizProjetor21/03/2024912,88
14RScarla.rodriguesRafaelMonitor06/05/2024760,32
15RScarla.rodriguesJulianaProcessador23/06/2024980,65
16PRfernando.santosDiegoCâmera25/03/20241215,99
17PRfernando.santosFernandaMouse10/05/202490,75
18PRfernando.santosFelipePlaca Mãe27/06/2024850,34
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340,45
20SCjuliana.almeidaSilviaImpressora14/05/2024670,99
21SPana.martinsRobertoTv LED01/04/20241450,75
22SPana.martinsBrunoProjetor18/05/20241045,65
23RSpaulo.silvaVanessaTablet04/04/2024890,34
24RSpaulo.silvaCarlaCâmera22/05/20241150,00
25PRfernanda.costaGustavoSmartphone08/04/20241230,67
26PRfernanda.costaJoãoCaixa de Som26/05/2024315,99
27SCrenata.souzaHelenaHD Externo12/04/2024720,50
28SCrenata.souzaLucasTv LED30/05/20241620,45
29RSjose.mendesFelipeMini PC16/04/20241342,88
30RSjose.mendesPedroTablet03/06/2024750,88

Por que escolher window functions para comparar e analisar dados?

  • Comparar valores: Você pode comparar o valor de uma linha com os valores das linhas anteriores ou posteriores. Por exemplo, descobrir se as vendas de um produto estão aumentando ou diminuindo ao longo do tempo.
  • Calcular totais acumulados: Calcular o total acumulado de vendas até determinada data, ou o número total de clientes por região.
  • Ranking: Classificar os registros dentro de um grupo, como os 10 produtos mais vendidos em cada categoria.
  • Análise de séries temporais: Analisar tendências e padrões em dados que se alteram ao longo do tempo.
  • Identificar lacunas: em uma sequência de dados, como uma lista de datas sem registros.

Principais Window Functions no PostgreSQL

  • ROW_NUMBER(): Numera as linhas dentro da partição.
  • RANK(): Atribui uma classificação às linhas, permitindo empates.
  • DENSE_RANK(): Similar ao RANK(), mas sem pular números após empates.
  • NTILE(): Divide as linhas em um número específico de grupos.
  • LAG() e LEAD(): Permitem acessar dados de linhas anteriores ou posteriores.
  • FIRST_VALUE() e LAST_VALUE(): Retornam o primeiro ou último valor dentro da partição.
  • CUME_DIST(): Retorna a distribuição cumulativa de uma linha dentro de um conjunto de resultados. É o percentual de linhas com valores menores ou iguais ao valor atual.
  • PERCENT_RANK(): Calcula a posição relativa de uma linha dentro de um conjunto de resultados como uma fração. Diferente de CUME_DIST, o PERCENT_RANK calcula a posição como a diferença entre a classificação da linha e 1, dividido pelo número de linhas menos 1.
  • NTH_VALUE(): Retorna o valor da n-ésima2 linha dentro de uma janela. Se o n for maior que o número de linhas, o resultado será NULL.

:. Vamos a Prática .:

Preparando o Ambiente: Criação da Tabela

Para começar, vamos criar uma tabela de exemplo que será usada ao longo deste guia. Ela simula um conjunto de dados de vendas por produto em diferentes regiões.

create table vendas (
    id serial primary key,
    estado varchar(2) not null,
    vendedor varchar(50) not null,
    cliente varchar(50) not null,
    produto varchar(50) not null,
    data date not null,
    valor numeric(10, 2) not null
);

create index idx_vendas_estado on vendas (estado);

create index idx_vendas_vendedor on vendas (vendedor);

create index idx_vendas_data on vendas (data);

create index idx_vendas_cliente on vendas (cliente);

create index idx_vendas_produto on vendas (produto);

insert into vendas (estado, vendedor, cliente, produto, data, valor)
    values ('SP', 'valeria.sacks', 'José', 'Teclado', '2024-03-10', 120.45),
    ('SP', 'valeria.sacks', 'Larissa', 'Memoria RAM', '2024-04-20', 590.20),
    ('SP', 'valeria.sacks', 'Camila', 'Smartphone', '2024-06-07', 1340.20),
    ('PR', 'roberto.ferreira', 'Eduardo', 'Monitor', '2024-03-12', 750.32),
    ('PR', 'roberto.ferreira', 'Marcelo', 'Processador', '2024-04-24', 980.76),
    ('PR', 'roberto.ferreira', 'Rodrigo', 'HD Externo', '2024-06-11', 640.55),
    ('RS', 'aline.souza', 'Claudia', 'Mouse', '2024-03-15', 85.99),
    ('RS', 'aline.souza', 'Mariana', 'Placa Mãe', '2024-04-28', 810.45),
    ('RS', 'aline.souza', 'Simone', 'Mini PC', '2024-06-15', 1450.99),
    ('SC', 'marcos.lima', 'Leonardo', 'Impressora', '2024-03-18', 654.77),
    ('SC', 'marcos.lima', 'André', 'Teclado', '2024-05-02', 135.78),
    ('SC', 'marcos.lima', 'Bruno', 'Memoria RAM', '2024-06-19', 720.33),
    ('RS', 'carla.rodrigues', 'Beatriz', 'Projetor', '2024-03-21', 912.88),
    ('RS', 'carla.rodrigues', 'Rafael', 'Monitor', '2024-05-06', 760.32),
    ('RS', 'carla.rodrigues', 'Juliana', 'Processador', '2024-06-23', 980.65),
    ('PR', 'fernando.santos', 'Diego', 'Câmera', '2024-03-25', 1215.99),
    ('PR', 'fernando.santos', 'Fernanda', 'Mouse', '2024-05-10', 90.75),
    ('PR', 'fernando.santos', 'Felipe', 'Placa Mãe', '2024-06-27', 850.34),
    ('SC', 'juliana.almeida', 'Tatiana', 'Caixa de Som', '2024-03-28', 340.45),
    ('SC', 'juliana.almeida', 'Silvia', 'Impressora', '2024-05-14', 670.99),
    ('SP', 'ana.martins', 'Roberto', 'Tv LED', '2024-04-01', 1450.75),
    ('SP', 'ana.martins', 'Bruno', 'Projetor', '2024-05-18', 1045.65),
    ('RS', 'paulo.silva', 'Vanessa', 'Tablet', '2024-04-04', 890.34),
    ('RS', 'paulo.silva', 'Carla', 'Câmera', '2024-05-22', 1150.00),
    ('PR', 'fernanda.costa', 'Gustavo', 'Smartphone', '2024-04-08', 1230.67),
    ('PR', 'fernanda.costa', 'João', 'Caixa de Som', '2024-05-26', 315.99),
    ('SC', 'renata.souza', 'Helena', 'HD Externo', '2024-04-12', 720.50),
    ('SC', 'renata.souza', 'Lucas', 'Tv LED', '2024-05-30', 1620.45),
    ('RS', 'jose.mendes', 'Felipe', 'Mini PC', '2024-04-16', 1342.88),
    ('RS', 'jose.mendes', 'Pedro', 'Tablet', '2024-06-03', 750.88);

Claúsulas OVER e PARTITION BY

OVER Define a janela de linhas sobre as quais a função será aplicada. Ela permite que uma função de janela, como ROW_NUMBER(), RANK(), SUM(), ou qualquer outra função agregada, seja calculada ao longo de um conjunto específico de linhas, sem a necessidade de agrupar ou consolidar os dados.

PARTITION BY é uma parte opcional da cláusula OVER. Ela divide o conjunto de resultados em partições (grupos) de linhas e a função de janela é aplicada separadamente a cada partição. Cada partição pode ser vista como um subconjunto da tabela sobre o qual a função é calculada individualmente. Quando PARTITION BY não é usado, a função de janela é aplicada sobre todas as linhas do conjunto de resultados.

ORDER BY dentro da cláusula OVER especifica a ordem em que as linhas dentro de cada partição (ou do conjunto de resultados completo, se PARTITION BY não for usado) são processadas. Isso é especialmente útil para funções de janela que dependem da ordenação, como ROW_NUMBER(), RANK(), e LAG(). Se ORDER BY não for especificado, as linhas dentro da partição não têm uma ordem definida.

FRAME (também chamado de frame_clause) é uma extensão opcional da cláusula OVER que define um subconjunto das linhas na partição, ao qual a função de janela será aplicada. É útil para limitar o cálculo a um grupo específico de linhas em torno da linha atual, ao invés de aplicá-lo a todas as linhas da partição.

Tipos de cláusulas de frame incluem:

  • ROWS: Define o frame em termos de um número específico de linhas antes ou depois da linha atual.
  • RANGE: Define o frame em termos de valores lógicos relacionados à linha atual, como todos os valores anteriores ou seguintes até um determinado ponto.

ROW_NUM

A função ROW_NUMBER() é útil em vários contextos, especialmente quando você precisa atribuir números únicos a linhas dentro de grupos específicos, com base em uma ordem determinada. No exemplo fornecido, a função ROW_NUMBER() é utilizada para numerar as vendas em cada estado de acordo com a data da venda. Aqui estão algumas utilidades específicas para o uso de ROW_NUMBER() nesse contexto:

  • Ordenação Cronológica: Permite que você atribua uma numeração sequencial às vendas em cada estado, com base na data. Isso é útil para acompanhar a ordem das vendas ao longo do tempo dentro de um estado, permitindo que você veja qual venda foi a primeira, a segunda, etc.
  • Análise de Frequência: Com a numeração sequencial, você pode facilmente analisar padrões e tendências de vendas ao longo do tempo em cada estado. Por exemplo, ao olhar para as vendas numeradas como 1, 2, 3, etc., você pode identificar se as vendas tendem a aumentar ou diminuir em determinados períodos.
  • Seleção de Vendas Recorrentes: Se você quiser selecionar apenas a primeira venda de cada estado, por exemplo, poderia usar um WHERE num_venda_por_estado = 1. Isso é útil em relatórios que precisam mostrar apenas o primeiro registro de cada grupo.
  • Relatórios Detalhados: Em relatórios detalhados, você pode querer incluir uma numeração que ajude a organizar e compreender as vendas dentro de cada estado. A numeração sequencial pode facilitar a leitura e interpretação dos dados.
  • Mudança de Status: Se você tivesse uma coluna adicional para indicar se um cliente fez outra compra, poderia usar ROW_NUMBER() para identificar a primeira, segunda, terceira compra, e assim por diante. Isso permite analisar como o comportamento de compra evolui ao longo do tempo.
  • Subconsultas e Junções: A numeração sequencial com ROW_NUMBER() pode ser usada em subconsultas para operações mais complexas, como juntar apenas as vendas mais recentes ou as mais antigas em cada estado com outros conjuntos de dados.
  • Histórico de Vendas: Se você precisar manter um histórico detalhado e rastreável das vendas, a numeração cronológica é essencial. Você pode, por exemplo, criar registros históricos onde a posição cronológica da venda dentro de um estado é um fator importante.

Neste exemplo, vamos gerar uma numeração sequencial para as vendas, ordenando-as pela data. O ROW_NUMBER() será reiniciado para cada estado usando a cláusula PARTITION BY.

select
    id,
    estado,
    vendedor,
    cliente,
    produto,
    "data",
    valor,
    row_number() over (partition by estado order by data) as num_venda_por_estado
from
    public.vendas

Resultado:

#IdUFVendedorClienteProdutoDataValorNº Venda/Estado
4PRroberto.ferreiraEduardoMonitor12/03/2024750,321
16PRfernando.santosDiegoCâmera25/03/20241.215,992
25PRfernanda.costaGustavoSmartphone08/04/20241.230,673
5PRroberto.ferreiraMarceloProcessador24/04/2024980,764
17PRfernando.santosFernandaMouse10/05/202490,755
26PRfernanda.costaJoãoCaixa de Som26/05/2024315,996
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640,557
18PRfernando.santosFelipePlaca Mãe27/06/2024850,348
7RSaline.souzaClaudiaMouse15/03/202485,991
13RScarla.rodriguesBeatrizProjetor21/03/2024912,882
23RSpaulo.silvaVanessaTablet04/04/2024890,343
29RSjose.mendesFelipeMini PC16/04/20241.342,884
8RSaline.souzaMarianaPlaca Mãe28/04/2024810,455
14RScarla.rodriguesRafaelMonitor06/05/2024760,326
24RSpaulo.silvaCarlaCâmera22/05/20241.1507
30RSjose.mendesPedroTablet03/06/2024750,888
9RSaline.souzaSimoneMini PC15/06/20241.450,999
15RScarla.rodriguesJulianaProcessador23/06/2024980,6510
10SCmarcos.limaLeonardoImpressora18/03/2024654,771
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340,452
27SCrenata.souzaHelenaHD Externo12/04/2024720,53
11SCmarcos.limaAndréTeclado02/05/2024135,784
20SCjuliana.almeidaSilviaImpressora14/05/2024670,995
28SCrenata.souzaLucasTv LED30/05/20241.620,456
12SCmarcos.limaBrunoMemoria RAM19/06/2024720,337
1SPvaleria.sacksJoséTeclado10/03/2024120,451
21SPana.martinsRobertoTv LED01/04/20241.450,752
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590,23
22SPana.martinsBrunoProjetor18/05/20241.045,654
3SPvaleria.sacksCamilaSmartphone07/06/20241.340,205
Explicação:
  • PARTITION BY estado: Divide as vendas por estado, criando partições.
  • ORDER BY data: Ordena as vendas dentro de cada partição (estado) pela data.
  • ROW_NUMBER(): Gera uma numeração sequencial dentro de cada partição.
RANK

A função RANK() é utilizada em cenários onde você deseja classificar as linhas dentro de grupos específicos, com base em uma determinada ordem, e lidar com empates na classificação. Ao contrário de ROW_NUMBER(), que atribui números sequenciais únicos, RANK() atribui a mesma classificação a linhas com valores idênticos, deixando lacunas na sequência para as próximas classificações. Vamos explorar algumas utilidades da função RANK():

  • Classificação com Empates: A função RANK() é útil quando você precisa atribuir classificações às linhas dentro de grupos, levando em conta empates. Por exemplo, se duas vendas em um estado possuem o mesmo valor, ambas receberão a mesma classificação e a próxima venda receberá a classificação correspondente com uma lacuna.
  • Análise de Desempenho: Em relatórios de vendas, RANK() pode ser utilizado para analisar o desempenho relativo de produtos ou vendedores, especialmente quando há múltiplas vendas com valores semelhantes. Com isso, é possível identificar facilmente quais vendas se destacam e quais estão em posições intermediárias.
  • Identificação de Posições: Ao utilizar RANK() em subconsultas, você pode identificar as posições relativas de elementos em um conjunto de dados, o que pode ser útil para filtrar ou comparar dados em junções ou em análises temporais.
  • Relatórios de Liderança: Em rankings ou relatórios de liderança, onde você precisa mostrar a posição de um cliente, produto ou vendedor em relação a outros dentro de um grupo, RANK() é uma escolha ideal. Se dois ou mais itens estão empatados, eles aparecem na mesma posição, refletindo de forma precisa a igualdade entre eles.

Vamos considerar um exemplo onde você deseja classificar as vendas por estado e valor, utilizando a função RANK() para gerar uma classificação das vendas em cada estado:

select
    id,
    estado,
    cliente,
    produto,
    data,
    valor,
    rank() over (partition by estado order by valor desc) as rank_venda
from
    vendas

Resultado:

#IdUFVendedorClienteProdutoDataValorRank
25PRfernanda.costaGustavoSmartphone08/04/20241230.671
16PRfernando.santosDiegoCâmera25/03/20241215.992
5PRroberto.ferreiraMarceloProcessador24/04/2024980.763
18PRfernando.santosFelipePlaca Mãe27/06/2024850.344
4PRroberto.ferreiraEduardoMonitor12/03/2024750.325
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640.556
26PRfernanda.costaJoãoCaixa de Som26/05/2024315.997
17PRfernando.santosFernandaMouse10/05/202490.758
9RSaline.souzaSimoneMini PC15/06/20241450.991
29RSjose.mendesFelipeMini PC16/04/20241342.882
24RSpaulo.silvaCarlaCâmera22/05/20241150.003
15RScarla.rodriguesJulianaProcessador23/06/2024980.654
13RScarla.rodriguesBeatrizProjetor21/03/2024912.885
23RSpaulo.silvaVanessaTablet04/04/2024890.346
8RSaline.souzaMarianaPlaca Mãe28/04/2024810.457
14RScarla.rodriguesRafaelMonitor06/05/2024760.328
30RSjose.mendesPedroTablet03/06/2024750.889
7RSaline.souzaClaudiaMouse15/03/202485.9910
28SCrenata.souzaLucasTv LED30/05/20241620.451
27SCrenata.souzaHelenaHD Externo12/04/2024720.502
12SCmarcos.limaBrunoMemoria RAM19/06/2024720.333
20SCjuliana.almeidaSilviaImpressora14/05/2024670.994
10SCmarcos.limaLeonardoImpressora18/03/2024654.775
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340.456
11SCmarcos.limaAndréTeclado02/05/2024135.787
21SPana.martinsRobertoTv LED01/04/20241450.751
3SPvaleria.sacksCamilaSmartphone07/06/20241340.202
22SPana.martinsBrunoProjetor18/05/20241045.653
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590.204
1SPvaleria.sacksJoséTeclado10/03/2024120.455
Explicação:

Nesse exemplo, o PARTITION BY estado divide as vendas por estado, e o ORDER BY valor DESC classifica as vendas dentro de cada estado do maior para o menor valor. A função RANK() atribui um número de classificação a cada venda dentro do grupo.

NTILE

A função NTILE() é usada para dividir um conjunto de resultados em um número especificado de “buckets” ou “grupos”, distribuindo as linhas o mais igualmente possível entre esses grupos. Diferente de RANK(), NTILE() atribui um número sequencial para as linhas, correspondendo ao bucket no qual a linha está. Isso pode ser útil para criar distribuições percentuais ou segmentar dados em partes iguais.

Aqui está uma versão do exemplo utilizando a função NTILE():

select
    id,
    estado,
    vendedor,
    cliente,
    produto,
    data,
    valor,
    ntile(3) over (partition by estado order by valor desc) as ntile_venda
from
    vendas

Resultado:

#IdUFVendedorClienteProdutoDataValorNTILE
25PRfernanda.costaGustavoSmartphone08/04/20241230.671
16PRfernando.santosDiegoCâmera25/03/20241215.991
5PRroberto.ferreiraMarceloProcessador24/04/2024980.761
18PRfernando.santosFelipePlaca Mãe27/06/2024850.342
4PRroberto.ferreiraEduardoMonitor12/03/2024750.322
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640.552
26PRfernanda.costaJoãoCaixa de Som26/05/2024315.993
17PRfernando.santosFernandaMouse10/05/202490.753
9RSaline.souzaSimoneMini PC15/06/20241450.991
29RSjose.mendesFelipeMini PC16/04/20241342.881
24RSpaulo.silvaCarlaCâmera22/05/20241150.001
15RScarla.rodriguesJulianaProcessador23/06/2024980.651
13RScarla.rodriguesBeatrizProjetor21/03/2024912.882
23RSpaulo.silvaVanessaTablet04/04/2024890.342
8RSaline.souzaMarianaPlaca Mãe28/04/2024810.452
14RScarla.rodriguesRafaelMonitor06/05/2024760.323
30RSjose.mendesPedroTablet03/06/2024750.883
7RSaline.souzaClaudiaMouse15/03/202485.993
28SCrenata.souzaLucasTv LED30/05/20241620.451
27SCrenata.souzaHelenaHD Externo12/04/2024720.501
12SCmarcos.limaBrunoMemoria RAM19/06/2024720.331
20SCjuliana.almeidaSilviaImpressora14/05/2024670.992
10SCmarcos.limaLeonardoImpressora18/03/2024654.772
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340.453
11SCmarcos.limaAndréTeclado02/05/2024135.783
21SPana.martinsRobertoTv LED01/04/20241450.751
3SPvaleria.sacksCamilaSmartphone07/06/20241340.201
22SPana.martinsBrunoProjetor18/05/20241045.652
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590.202
1SPvaleria.sacksJoséTeclado10/03/2024120.453
Explicação:
  • A função NTILE(3) divide as vendas dentro de cada estado em 3 grupos.
  • PARTITION BY estado agrupa as vendas por estado.
  • ORDER BY valor DESC garante que as vendas com maior valor estejam nos primeiros grupos.

Essa abordagem é útil para dividir as vendas de cada estado em partes iguais, permitindo ver, por exemplo, o “terço superior”, “terço intermediário” e “terço inferior” de vendas.

LAG & LEAD

A função LAG() permite acessar os valores de linhas anteriores à linha atual dentro de uma janela, enquanto a função LEAD() faz o mesmo para as linhas posteriores. Ambas são úteis quando você precisa comparar valores anteriores ou seguintes em uma série de dados sem usar subconsultas complexas.

Aqui está a versão do exemplo utilizando as funções LAG() e LEAD():

select
    id,
    estado,
    vendedor,
    cliente,
    produto,
    data,
    valor,
    lag(valor, 1) over (partition by estado order by valor desc) as valor_anterior,
    lead(valor, 1) over (partition by estado order by valor desc) as valor_proximo
from
    vendas

Resultado:

#IdUFVendedorClienteProdutoDataValorAnteriorPróximo
25PRfernanda.costaGustavoSmartphone08/04/20241230.67NULL1215.99
16PRfernando.santosDiegoCâmera25/03/20241215.991230.67980.76
5PRroberto.ferreiraMarceloProcessador24/04/2024980.761215.99850.34
18PRfernando.santosFelipePlaca Mãe27/06/2024850.34980.76750.32
4PRroberto.ferreiraEduardoMonitor12/03/2024750.32850.34640.55
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640.55750.32315.99
26PRfernanda.costaJoãoCaixa de Som26/05/2024315.99640.5590.75
17PRfernando.santosFernandaMouse10/05/202490.75315.99NULL
9RSaline.souzaSimoneMini PC15/06/20241450.99NULL1342.88
29RSjose.mendesFelipeMini PC16/04/20241342.881450.991150.00
24RSpaulo.silvaCarlaCâmera22/05/20241150.001342.88980.65
15RScarla.rodriguesJulianaProcessador23/06/2024980.651150.00912.88
13RScarla.rodriguesBeatrizProjetor21/03/2024912.88980.65890.34
23RSpaulo.silvaVanessaTablet04/04/2024890.34912.88810.45
8RSaline.souzaMarianaPlaca Mãe28/04/2024810.45890.34760.32
14RScarla.rodriguesRafaelMonitor06/05/2024760.32810.45750.88
30RSjose.mendesPedroTablet03/06/2024750.88760.3285.99
7RSaline.souzaClaudiaMouse15/03/202485.99750.88NULL
28SCrenata.souzaLucasTv LED30/05/20241620.45NULL720.50
27SCrenata.souzaHelenaHD Externo12/04/2024720.501620.45720.33
12SCmarcos.limaBrunoMemoria RAM19/06/2024720.33720.50670.99
20SCjuliana.almeidaSilviaImpressora14/05/2024670.99720.33654.77
10SCmarcos.limaLeonardoImpressora18/03/2024654.77670.99340.45
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340.45654.77135.78
11SCmarcos.limaAndréTeclado02/05/2024135.78340.45NULL
21SPana.martinsRobertoTv LED01/04/20241450.75NULL1340.20
3SPvaleria.sacksCamilaSmartphone07/06/20241340.201450.751045.65
22SPana.martinsBrunoProjetor18/05/20241045.651340.20590.20
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590.201045.65120.45
1SPvaleria.sacksJoséTeclado10/03/2024120.45590.20NULL
Explicação:
  • A função LAG() retorna o valor da linha anterior dentro da partição de acordo com a ordenação. Neste exemplo, LAG(valor, 1) traz o valor da venda anterior para cada estado, ordenado por valor.
  • A função LEAD() retorna o valor da próxima linha. Aqui, LEAD(valor, 1) traz o valor da próxima venda.
  • PARTITION BY estado agrupa as vendas por estado, e ORDER BY valor DESC garante a ordem decrescente dos valores de vendas.

Essas funções podem ser úteis para comparar os valores das vendas com as vendas imediatamente anteriores ou seguintes no mesmo estado, facilitando a análise de mudanças ou tendências.

FIRST_VALUE & LAST_VALUE

A função FIRST_VALUE() retorna o primeiro valor de uma janela de resultados com base na ordenação especificada, enquanto a função LAST_VALUE() retorna o último valor da janela de resultados. Ambas são úteis para capturar valores de referência no início ou no final de um conjunto de dados particionado.

Aqui está a versão do exemplo utilizando as funções FIRST_VALUE() e LAST_VALUE():

select
    id,
    estado,
    vendedor,
    cliente,
    produto,
    data,
    valor,
    first_value(valor) over (partition by estado order by valor desc) as primeira_venda,
    last_value(valor) over (partition by estado order by valor desc rows between unbounded preceding and unbounded following) as ultima_venda
from
    vendas

Resultado:

#IdUFVendedorClienteProdutoDataValorP. VendaU. Venda
25PRfernanda.costaGustavoSmartphone08/04/20241230.671230.6790.75
16PRfernando.santosDiegoCâmera25/03/20241215.991230.6790.75
5PRroberto.ferreiraMarceloProcessador24/04/2024980.761230.6790.75
18PRfernando.santosFelipePlaca Mãe27/06/2024850.341230.6790.75
4PRroberto.ferreiraEduardoMonitor12/03/2024750.321230.6790.75
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640.551230.6790.75
26PRfernanda.costaJoãoCaixa de Som26/05/2024315.991230.6790.75
17PRfernando.santosFernandaMouse10/05/202490.751230.6790.75
9RSaline.souzaSimoneMini PC15/06/20241450.991450.9985.99
29RSjose.mendesFelipeMini PC16/04/20241342.881450.9985.99
24RSpaulo.silvaCarlaCâmera22/05/20241150.001450.9985.99
15RScarla.rodriguesJulianaProcessador23/06/2024980.651450.9985.99
13RScarla.rodriguesBeatrizProjetor21/03/2024912.881450.9985.99
23RSpaulo.silvaVanessaTablet04/04/2024890.341450.9985.99
8RSaline.souzaMarianaPlaca Mãe28/04/2024810.451450.9985.99
14RScarla.rodriguesRafaelMonitor06/05/2024760.321450.9985.99
30RSjose.mendesPedroTablet03/06/2024750.881450.9985.99
7RSaline.souzaClaudiaMouse15/03/202485.991450.9985.99
28SCrenata.souzaLucasTv LED30/05/20241620.451620.45135.78
27SCrenata.souzaHelenaHD Externo12/04/2024720.501620.45135.78
12SCmarcos.limaBrunoMemoria RAM19/06/2024720.331620.45135.78
20SCjuliana.almeidaSilviaImpressora14/05/2024670.991620.45135.78
10SCmarcos.limaLeonardoImpressora18/03/2024654.771620.45135.78
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340.451620.45135.78
11SCmarcos.limaAndréTeclado02/05/2024135.781620.45135.78
21SPana.martinsRobertoTv LED01/04/20241450.751450.75120.45
3SPvaleria.sacksCamilaSmartphone07/06/20241340.201450.75120.45
22SPana.martinsBrunoProjetor18/05/20241045.651450.75120.45
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590.201450.75120.45
1SPvaleria.sacksJoséTeclado10/03/2024120.451450.75120.45
Explicação:
  • A função FIRST_VALUE(valor) retorna o maior valor (primeira venda) dentro da partição estado, ordenada por valor DESC.
  • A função LAST_VALUE(valor) retorna o menor valor (última venda) dentro da mesma partição.
  • PARTITION BY estado agrupa as vendas por estado.
  • ORDER BY valor DESC ordena as vendas por valor decrescente, de modo que FIRST_VALUE() traga o maior valor e LAST_VALUE() o menor valor.
  • O intervalo de linhas ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING é necessário para garantir que LAST_VALUE() retorne o valor da última linha da janela e não o último valor da janela atual, que por padrão se refere à linha atual.

Essas funções são úteis para capturar os valores extremos em cada grupo de dados, como a primeira e a última venda de um estado, facilitando análises como identificar o maior e o menor valor de venda em um conjunto.

CUME_DIST

A função CUME_DIST() calcula a “distribuição cumulativa” de um valor em um conjunto de dados. Ela retorna a proporção de linhas com valores menores ou iguais ao valor da linha atual, variando entre 0 e 1. Isso é útil quando você quer entender a posição relativa de uma linha em um conjunto de dados.

Aqui está a versão do exemplo utilizando a função CUME_DIST():

select
    id,
    estado,
    vendedor,
    cliente,
    produto,
    data,
    valor,
    cume_dist() over (partition by estado order by valor desc) as cume_dist_venda
from
    vendas

Resultado:

#IDUFVendedorClienteProdutoDataValorCume
25PRfernanda.costaGustavoSmartphone08/04/20241230.670,125
16PRfernando.santosDiegoCâmera25/03/20241215.990,250
5PRroberto.ferreiraMarceloProcessador24/04/2024980.760,375
18PRfernando.santosFelipePlaca Mãe27/06/2024850.340,500
4PRroberto.ferreiraEduardoMonitor12/03/2024750.320,625
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640.550,750
26PRfernanda.costaJoãoCaixa de Som26/05/2024315.990,875
17PRfernando.santosFernandaMouse10/05/202490.751
9RSaline.souzaSimoneMini PC15/06/20241450.990,100
29RSjose.mendesFelipeMini PC16/04/20241342.880,200
24RSpaulo.silvaCarlaCâmera22/05/20241150.000,300
15RScarla.rodriguesJulianaProcessador23/06/2024980.650,400
13RScarla.rodriguesBeatrizProjetor21/03/2024912.880,500
23RSpaulo.silvaVanessaTablet04/04/2024890.340,600
8RSaline.souzaMarianaPlaca Mãe28/04/2024810.450,700
14RScarla.rodriguesRafaelMonitor06/05/2024760.320,800
30RSjose.mendesPedroTablet03/06/2024750.880,900
7RSaline.souzaClaudiaMouse15/03/202485.991
28SCrenata.souzaLucasTv LED30/05/20241620.450,143
27SCrenata.souzaHelenaHD Externo12/04/2024720.500,286
12SCmarcos.limaBrunoMemoria RAM19/06/2024720.330,429
20SCjuliana.almeidaSilviaImpressora14/05/2024670.990,571
10SCmarcos.limaLeonardoImpressora18/03/2024654.770,714
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340.450,857
11SCmarcos.limaAndréTeclado02/05/2024135.781
21SPana.martinsRobertoTv LED01/04/20241450.750,200
3SPvaleria.sacksCamilaSmartphone07/06/20241340.200,400
22SPana.martinsBrunoProjetor18/05/20241045.650,600
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590.200,800
1SPvaleria.sacksJoséTeclado10/03/2024120.451
Explicação:
  • A função FIRST_VALUE(valor) retorna o maior valor (primeira venda) dentro da partição estado, ordenada por valor DESC.
  • A função LAST_VALUE(valor) retorna o menor valor (última venda) dentro da mesma partição.
  • PARTITION BY estado agrupa as vendas por estado.
  • ORDER BY valor DESC ordena as vendas por valor decrescente, de modo que FIRST_VALUE() traga o maior valor e LAST_VALUE() o menor valor.
  • O intervalo de linhas ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING é necessário para garantir que LAST_VALUE() retorne o valor da última linha da janela e não o último valor da janela atual, que por padrão se refere à linha atual.

Essas funções são úteis para capturar os valores extremos em cada grupo de dados, como a primeira e a última venda de um estado, facilitando análises como identificar o maior e o menor valor de venda em um conjunto.

PERCENT_RANK

A função PERCENT_RANK() calcula a posição relativa de uma linha em relação ao conjunto de dados, expressa como um valor percentual entre 0 e 1. Ao contrário de CUME_DIST(), a PERCENT_RANK() distribui as linhas de forma que a primeira linha sempre tenha valor 0 e a última linha tenha valor 1 (ou próxima de 1, dependendo do número de linhas).

Aqui está a versão do exemplo utilizando a função PERCENT_RANK():

select
    id,
    estado,
    vendedor,
    cliente,
    produto,
    data,
    valor,
    percent_rank() over (partition by estado order by valor desc) as percent_rank_venda
from
    vendas

Resultado:

#IdUFVendedorClienteProdutoDataValor% Rank
25PRfernanda.costaGustavoSmartphone08/04/20241230.670
16PRfernando.santosDiegoCâmera25/03/20241215.990,143
5PRroberto.ferreiraMarceloProcessador24/04/2024980.760,286
18PRfernando.santosFelipePlaca Mãe27/06/2024850.340,429
4PRroberto.ferreiraEduardoMonitor12/03/2024750.320,571
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640.550,714
26PRfernanda.costaJoãoCaixa de Som26/05/2024315.990,857
17PRfernando.santosFernandaMouse10/05/202490.751
9RSaline.souzaSimoneMini PC15/06/20241450.990
29RSjose.mendesFelipeMini PC16/04/20241342.880,111
24RSpaulo.silvaCarlaCâmera22/05/20241150.000,222
15RScarla.rodriguesJulianaProcessador23/06/2024980.650,333
13RScarla.rodriguesBeatrizProjetor21/03/2024912.880,444
23RSpaulo.silvaVanessaTablet04/04/2024890.340,556
8RSaline.souzaMarianaPlaca Mãe28/04/2024810.450,667
14RScarla.rodriguesRafaelMonitor06/05/2024760.320,778
30RSjose.mendesPedroTablet03/06/2024750.880,889
7RSaline.souzaClaudiaMouse15/03/202485.991
28SCrenata.souzaLucasTv LED30/05/20241620.450
27SCrenata.souzaHelenaHD Externo12/04/2024720.500,167
12SCmarcos.limaBrunoMemoria RAM19/06/2024720.330,333
20SCjuliana.almeidaSilviaImpressora14/05/2024670.990,5
10SCmarcos.limaLeonardoImpressora18/03/2024654.770,667
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340.450,833
11SCmarcos.limaAndréTeclado02/05/2024135.781
21SPana.martinsRobertoTv LED01/04/20241450.750
3SPvaleria.sacksCamilaSmartphone07/06/20241340.200,25
22SPana.martinsBrunoProjetor18/05/20241045.650,5
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590.200,75
1SPvaleria.sacksJoséTeclado10/03/2024120.451
Explicação:
  • A função PERCENT_RANK() retorna a posição percentual de uma linha em relação ao conjunto de dados dentro de uma partição.
  • PARTITION BY estado agrupa as vendas por estado.
  • ORDER BY valor DESC organiza as vendas por valor decrescente dentro de cada estado.
  • O cálculo é feito considerando o número total de linhas na partição. A primeira linha de cada partição tem o valor 0, e a última linha se aproxima de 1 (ou é 1 se houver mais de uma linha).

Essa função é útil para determinar a posição percentual de uma venda em relação às outras dentro de um estado, proporcionando uma visão da classificação relativa dentro do grupo de vendas.

NTH_VALUE

A função NTH_VALUE() retorna o valor da n-ésima linha de uma janela de resultados, onde o número n é especificado pelo usuário. Ela é útil quando você deseja capturar um valor específico em uma sequência ordenada de linhas dentro de um grupo.

Aqui está a versão do exemplo utilizando a função NTH_VALUE() para obter o terceiro maior valor de cada grupo de vendas por estado:

select
    id,
    estado,
    vendedor,
    cliente,
    produto,
    data,
    valor,
    nth_value(valor, 3) over (partition by estado order by valor desc rows between unbounded preceding and unbounded following) as terceira_venda
from
    vendas

Resultado:

#IdUFVendedorClienteProdutoDataValorT. Venda
25PRfernanda.costaGustavoSmartphone08/04/20241230.67980.76
16PRfernando.santosDiegoCâmera25/03/20241215.99980.76
5PRroberto.ferreiraMarceloProcessador24/04/2024980.76980.76
18PRfernando.santosFelipePlaca Mãe27/06/2024850.34980.76
4PRroberto.ferreiraEduardoMonitor12/03/2024750.32980.76
6PRroberto.ferreiraRodrigoHD Externo11/06/2024640.55980.76
26PRfernanda.costaJoãoCaixa de Som26/05/2024315.99980.76
17PRfernando.santosFernandaMouse10/05/202490.75980.76
9RSaline.souzaSimoneMini PC15/06/20241450.991150.00
29RSjose.mendesFelipeMini PC16/04/20241342.881150.00
24RSpaulo.silvaCarlaCâmera22/05/20241150.001150.00
15RScarla.rodriguesJulianaProcessador23/06/2024980.651150.00
13RScarla.rodriguesBeatrizProjetor21/03/2024912.881150.00
23RSpaulo.silvaVanessaTablet04/04/2024890.341150.00
8RSaline.souzaMarianaPlaca Mãe28/04/2024810.451150.00
14RScarla.rodriguesRafaelMonitor06/05/2024760.321150.00
30RSjose.mendesPedroTablet03/06/2024750.881150.00
7RSaline.souzaClaudiaMouse15/03/202485.991150.00
28SCrenata.souzaLucasTv LED30/05/20241620.45720.33
27SCrenata.souzaHelenaHD Externo12/04/2024720.50720.33
12SCmarcos.limaBrunoMemoria RAM19/06/2024720.33720.33
20SCjuliana.almeidaSilviaImpressora14/05/2024670.99720.33
10SCmarcos.limaLeonardoImpressora18/03/2024654.77720.33
19SCjuliana.almeidaTatianaCaixa de Som28/03/2024340.45720.33
11SCmarcos.limaAndréTeclado02/05/2024135.78720.33
21SPana.martinsRobertoTv LED01/04/20241450.751045.65
3SPvaleria.sacksCamilaSmartphone07/06/20241340.201045.65
22SPana.martinsBrunoProjetor18/05/20241045.651045.65
2SPvaleria.sacksLarissaMemoria RAM20/04/2024590.201045.65
1SPvaleria.sacksJoséTeclado10/03/2024120.451045.65
Explicação:
  • A função NTH_VALUE(valor, 3) retorna o valor da terceira venda em cada grupo de estados, conforme ordenado pelo valor.
  • PARTITION BY estado agrupa as vendas por estado.
  • ORDER BY valor DESC organiza as vendas de cada estado por valor decrescente.
  • O intervalo ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING garante que a função olhe para toda a janela de valores dentro da partição.

Essa função é útil quando você deseja capturar o valor da n-ésima venda dentro de um grupo de vendas, possibilitando a análise de posições específicas em um conjunto de dados.


  1. Window Functions ⇨ São comandos que permitem realizar cálculos em um grupo de dados, sem precisar agrupar ou resumir esses dados. Pense nelas como uma maneira de adicionar “camadas” de informações extras, como uma contagem, soma ou média, a cada linha de uma tabela, enquanto mantém todos os detalhes originais dessa linha. É como ter uma visão mais ampla de cada linha dentro de um contexto maior. ↩︎
  2. n-ésima ⇨ Refere-se à posição de número n em uma sequência, onde “n” pode ser qualquer número inteiro. Por exemplo, a terceira posição em uma lista é a terceira “n-ésima” ↩︎

Referências

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *