Como fazer COUNT, SUM e agregar no DynamoDB
O DynamoDB tem exatamente uma agregação embutida: contar itens correspondentes com
Select=COUNT. Não há SUM, AVG, MIN ou MAX nativos. E mesmo a contagem que você
consegue obter lê (e cobra por) cada item que contou. Este guia cobre o que de fato é
suportado, as aproximações que as pessoas recorrem, e como rodar COUNT/SUM/AVG de
verdade sobre uma tabela quando você precisa deles.
O DynamoDB consegue fazer SUM, COUNT e funções de agregação?
Na maioria das vezes, não. A única agregação embutida do DynamoDB é o Select=COUNT, que retorna a contagem de itens correspondentes, mas ainda lê (e cobra por) cada item. Não há SUM, AVG, MIN ou MAX nativos, e o PartiQL também não adiciona nenhum. Para agregações de verdade com GROUP BY, faça a dobra no seu app, mantenha um contador, ou rode SQL no Workbench do DynoTable.
Select=COUNTretorna o número de itens correspondentes, mas o DynamoDB ainda lê cada item para produzi-lo — você paga o custo de leitura completo doScan/Query, não um custo barato de "contagem".- Não existe
SUM,AVG,MINouMAXnativo. As operações de leitura do DynamoDB retornam itens; elas não os dobram em um número. O PartiQL também não adiciona agregações. DescribeTable.ItemCounté grátis, mas aproximado e atualizado apenas "aproximadamente a cada seis horas" — ótimo para um bloco de dashboard, errado para qualquer coisa exata.- Para
COUNT/SUM/AVG/MIN/MAXexatos (comGROUP BY), agregue no seu app, mantenha um contador, ou rode no SQL Workbench do DynoTable (abaixo).
Contando itens: Select=COUNT
Tanto Query quanto Scan aceitam um parâmetro Select. Defina-o como COUNT e a
resposta carrega as contagens em vez dos itens:
aws dynamodb scan \
--table-name Orders \
--select COUNT \
--filter-expression "#s = :open" \
--expression-attribute-names '{"#s":"status"}' \
--expression-attribute-values '{":open":{"S":"OPEN"}}'A resposta te dá dois números (AWS: Counting the items in the results):
Count— "the number of items that remain, after a filter expression (if present) was applied."ScannedCount— "the number of items evaluated, before anyScanFilteris applied." Sem filtro,ScannedCounté igual aCount.
Se você tem apenas a partition key e precisa contar duplicatas dentro dela, a condição +
filtro que você passa é exatamente o que o
DynamoDB Expression Builder gera — os mapas
KeyConditionExpression, FilterExpression e ExpressionAttributeNames/Values
acima — sem escapar o JSON na mão.
Mais duas pegadinhas que mordem quem conta tabelas grandes:
- O limite de página de 1 MB ainda se aplica. "If the size of the
Scanresult set is larger than 1 MB,ScannedCountandCountrepresent only a partial count of the total items" (docs do AWS Scan). Você tem que paginar comLastEvaluatedKey→ExclusiveStartKeye manter um total acumulado para obter o número real — o mesmo loop coberto em paginação no DynamoDB. - Um
Queryestreito vence umScan.Select=COUNTem umQuerycobra apenas os itens da partição visada, não a tabela inteira. Se você consegue fixar uma partition key (tabela base ou um GSI), conte ali — é a diferença de custo Query-vs-Scan aplicada à contagem.
Select=COUNT vs ItemCount (e por que ele fica desatualizado)
DescribeTable retorna um ItemCount (e TableSizeBytes) de graça, sem custo de
leitura. A pegadinha está na
própria referência da API:
"DynamoDB updates this value approximately every six hours. Recent changes might not
be reflected in this value." Então ele pode ficar bem atrás do estado real da sua tabela.
Select=COUNT | DescribeTable.ItemCount | |
|---|---|---|
| Exatidão | Exato (para o conjunto correspondente) | Aproximado |
| Atualidade | Ao vivo | Atualizado ~a cada 6 horas |
| Custo | Lê + cobra cada item contado | Grátis (metadados) |
| Consegue filtrar / contar um subconjunto | Sim (filter expression) | Não — apenas a tabela inteira |
Use ItemCount para um palpite grosseiro de "qual o tamanho desta tabela" ou um bloco de
dashboard. Use Select=COUNT quando você precisa de um número exato, filtrado ou atual —
e aceite o custo de leitura. Para qualquer coisa de fato ao vivo e grátis, rastreie um
contador você mesmo (veja Padrões de agregação abaixo).
Por que não há SUM/AVG/MIN/MAX nativo
As operações de leitura do DynamoDB retornam itens. Não há planejador de consulta para
dobrar um conjunto de resultados em um escalar, então não há nada com que calcular um
SUM ou AVG. Contar é a única dobra que a API oferece, via Select=COUNT.
O PartiQL não muda isso. A
gramática do SELECT do PartiQL
é SELECT {{expression}} [, …] FROM {{table}}[.{{index}}] [WHERE …] [ORDER BY {{key}} …],
onde a expressão é "a projection formed from the * wildcard or a projection
list of one or more attribute names or document paths." Não há função de agregação
nem cláusula GROUP BY nessa gramática — e o ORDER BY recebe um {{key}},
documentado como "a hash key or a sort key to use to order returned results." Todo
SELECT do PartiQL ainda compila para um GetItem, Query ou Scan, então
SELECT SUM(total) FROM "Orders" simplesmente não é expressável. (Mais sobre o teto do
PartiQL em PartiQL vs SQL.)
Padrões de agregação (contadores, streams, no lado do app)
Como o DynamoDB não vai agregar por você, os padrões estabelecidos empurram o trabalho para outro lugar:
- Item contador mantido. Mantenha um item dedicado (ex.:
PK = "STATS#orders") eADDa um atributo numérico em cada escrita com umUpdateItem. Ler o agregado é então um únicoGetItem— exato e barato, mas você fica responsável pela lógica de incremento, sua consistência e a contenção se um contador for muito martelado. - DynamoDB Streams → agregador. Habilite um stream e ligue-o a uma Lambda que
atualiza totais acumulados (contagens, somas) conforme os itens mudam. Conforme as
docs do AWS Streams,
você pode configurar o
StreamViewTypedo stream para que cada registro carregue oNEW_AND_OLD_IMAGES— "both the new and the old images of the item" — o suficiente para manter agregados estiloSUMatualizados sem re-escanear. Os registros do stream estão sujeitos a um tempo de vida de 24 horas ("the stream records within a shard are removed automatically after 24 hours"), então o consumidor precisa acompanhar o ritmo. - Dobra no lado do app. Pagine pelos itens correspondentes e acumule o
SUM/AVG/MIN/MAXno seu próprio código. Correto, mas lê (e cobra por) cada item toda vez — o mesmo perfil de custo doSelect=COUNT, mais a transferência de dados. - Descarregar para analytics. Para agregação analítica pesada ou ad-hoc, exporte a tabela para o S3 e consulte-a com o Athena, ou faça stream dela para um warehouse. Conforme as docs de export para S3 da AWS, exportar "doesn't consume read capacity units" e permite que você "perform analytics and complex queries using AWS services such as Athena" — o caminho recomendado pela AWS quando você passou do ponto de agregação por requisição.
Cada um troca simplicidade por escrituração no momento da escrita (contadores, streams) ou
custo no momento da leitura (scans no lado do app). Nenhum padrão faz o próprio DynamoDB
computar um SUM de graça. A versão por agrupamento desse tradeoff — agregar por chave
em vez de sobre a tabela inteira — é um guia próprio:
DynamoDB GROUP BY.
Rodando COUNT/SUM/AVG no SQL Workbench do DynoTable
Quando você só precisa da resposta — "quantos pedidos OPEN, e qual o total deles" — sem escrever um loop de scan paginado ou uma Lambda, o SQL Workbench do DynoTable roda agregações de verdade. Ele materializa suas tabelas através do runtime real de Query/Scan do DynamoDB, e então roda seu SQL completo por cima: SQL dentro das regras de padrão de acesso do DynamoDB.
-- Runs in the DynoTable Workbench (NOT in PartiQL):
SELECT status,
COUNT(*) AS orders,
SUM(total) AS revenue,
AVG(total) AS avg_order,
MIN(total) AS smallest,
MAX(total) AS largest
FROM orders
GROUP BY status
ORDER BY revenue DESCIsso é COUNT, SUM, AVG, MIN, MAX, GROUP BY e ORDER BY — nenhum dos quais o
DynamoDB ou o PartiQL conseguem expressar — em uma única instrução. Essa é a mesma cunha
analítica que SQL para DynamoDB; para a história completa de
agrupamento veja DynamoDB GROUP BY.
O Workbench é honesto sobre o modelo de acesso por baixo, não um faz-de-conta-Postgres:
- As linhas ainda vêm pelo Query/Scan real do DynamoDB. Um
GROUP BYsobre uma tabela inteira ainda é umScanpor baixo — o Workbench expõe esse custo em vez de escondê-lo, o mesmo tradeoff Query-vs-Scan. - As agregações rodam sobre os atributos escalares materializados depois que as linhas chegam.
FAQ
Consigo contar itens no DynamoDB sem escanear?
Não exatamente. Para uma contagem exata e atual você precisa ler os itens —
Select=COUNT ainda cobra cada item contado. As únicas opções sem scan são o
DescribeTable.ItemCount aproximado (atualizado ~a cada 6 horas) ou um item contador que
você mantém você mesmo a cada escrita.
Como conto itens por um GSI?
Rode Query (ou Scan) contra o índice com Select=COUNT. Contar via uma partição
estreita de GSI é muito mais barato do que escanear a tabela base, porque você só lê os
itens daquela partição do índice — modele o índice em torno da contagem que você precisa.
O DescribeTable.ItemCount é preciso?
É aproximado. A
referência da API
afirma que o DynamoDB atualiza ItemCount e TableSizeBytes "approximately every six
hours," e "recent changes might not be reflected in this value." Não o use onde um número
exato ou ao vivo importa.
O DynamoDB consegue fazer SUM ou AVG?
Não nativamente, e não no PartiQL — a
gramática do SELECT do PartiQL
não tem funções de agregação. Agregue na sua aplicação, mantenha um contador
(opcionalmente via DynamoDB Streams), ou rode o SUM/AVG no SQL Workbench do
DynoTable.
Qual a diferença entre Count e ScannedCount?
ScannedCount é quantos itens o DynamoDB avaliou antes do seu filtro; Count é
quantos restam depois dele. Eles são iguais quando não há filter expression. Uma
diferença grande entre eles significa uma contagem ineficiente.
Precisa somar, tirar média ou agrupar seus dados do DynamoDB sem escrever um loop de scan? Baixe o DynoTable e rode em uma aba do Workbench. Comparando clientes primeiro? Veja onde ele se posiciona contra uma GUI comum do DynamoDB.