Intermediário9 min de leitura

DynamoDB GROUP BY: Como agregar

Não há GROUP BY em DynamoDB. Não há COUNT, SUM ou AVG também - não no nativo API, e não em PartiQL. DynamoDB é um valor-chave / armazenamento de documentos, não um mecanismo de análise, então a agregação é algo que você construir, não algo que o planejador de consultas faça por você.

Você pode GROUP BY em DynamoDB?

DynamoDB não tem GROUP BY, HAVING ou funções agregadas como COUNT, SUM e AVG - nem no nativo API e nem em PartiQL, cujo SELECT aceita apenas WHERE e ORDER BY. Você agrega pré-computando totais conforme alterações de dados (contadores atômicos ou+ Rollups Lambda) ou agrupando o lado do aplicativo após a leitura.

  • A gramática SELECT de DynamoDB PartiQL é SELECT… FROM… [WHERE …] [ORDER BY…]
  • Porque DynamoDB "não suporta nativamente operações de agregação como SUM ou COUNT entre itens", a orientação do próprio AWS é pré-calcular agregados conforme os dados mudam e armazenam os resultados como itens comuns (AWS: agregação materializada).
  • A alternativa – ler cada item e depois agregá-lo em seu aplicativo – funciona, mas você pague para ler a tabela inteira em cada consulta.
  • Para exploração única, SQL Workbench de DynoTable executa GROUP BY / COUNT / SUM / AVG diretamente em uma tabela ativa — os SQL DynamoDB's O terminal PartiQL é rejeitado.

Por que a agregação é difícil em DynamoDB

DynamoDB não possui mecanismo de agregação de tempo de varredura. Query e Scan retornam itens; eles não os dobram. Um Scan lê a tabela inteira 1 MB por vez, e o a capacidade que consome é baseada nos itens que lê, não nas linhas que você mantém - um FilterExpression é aplicado após a varredura, mas antes do retorno dos resultados, então restringe o conjunto de resultados sem diminuir a conta (AWS Scan API referência: um filtro “não consome nenhuma unidade adicional de capacidade de leitura”; a capacidade é baseada no item tamanho digitalizado, não devolvido). Não há gancho GROUP-BY para gastar uma quantia ou contar em primeiro lugar.

PartiQL não muda isso. PartiQL é um dialeto SQL-compatível sobre o mesmo motor, então ele herda os mesmos limites - é uma superfície de sintaxe, não um novo modelo de execução. A gramática SELECT documentada simplesmente não possui token GROUP BY. Para saber a diferença completa entre PartiQL e SQL real, consulte PartiQL vs SQL.

Pergunte onde está o seu agregado e quando ele é computado. Existem três respostas.

Padrão 1: agregação na gravação (contadores atômicos)

Se você conhece os grupos com antecedência – contagem por status, total por cliente, downloads por mês – mantenha um item de contador e atualize-o a cada gravação.

Use um ADDportanto, o incremento é atômico e seguro para simultaneidade. ADD funciona com números e conjuntos e evita a corrida leitura-modificação-gravação, então dois escritores incrementando o mesmo contador nunca se derrotam (AWS observa o ADD atômico "evita condições de corrida de leitura-modificação-gravação"):

UpdateItem
Key                         { pk: "STATS#orders", sk: "status#shipped" }
UpdateExpression            "ADD orderCount :one"
ExpressionAttributeValues   { ":one": 1 }

Este é o seu SELECT COUNT(*)… GROUP BY status - exceto que a contagem já está sentado lá como um item, legível em um milissegundo de um dígito GetItem. O compensação: você deve conhecer a chave de agrupamento no momento da gravação e acoplar a atualização do contador para o caminho de gravação. Se o aplicativo travar após a gravação, mas antes da atualização do contador, os dois ficam fora de sincronia - que é exatamente o modo de falha, o próximo padrão se desacopla.

Padrão 2: DynamoDB Streams + rollups Lambda

Quando você não deseja lógica de agregação no caminho de gravação — ou a gravação é um PutItem simples você não pode quebrar facilmente - mova-o para baixo. Este é o próprio AWS padrão recomendado, agregação materializada (AWS: Usando GSIs para consultas de agregação materializadas):

  1. O aplicativo grava o item bruto (um pedido, um download, um evento). Sem agregação lógica. 2.captura a gravação como um registro de fluxo.
  2. Um Lambda anexado ao fluxo lê o novo item, deriva o grupo (status, mês, categoria…) e ADDs ao item agregado correspondente com um atômico UpdateItem — que "evita condições de corrida de leitura-modificação-gravação" quando muitas invocações tocam o mesmo contador.
  3. Você consulta o agregado pré-calculado — geralmente por meio de um isso indexa apenas os itens de rollup, então "top 10 deste mês" é um Query com Limite 10.

Apenas os itens agregados carregam o atributo indexado (por exemplo, Month), para que as linhas brutas do evento sejam excluídas do índice automaticamente — "uma pequena fração do total de itens da tabela", que mantém o índice barato e a leitura rápida.

Isso desacopla a agregação do caminho de gravação e mantém as gravações simples, no custo de consistência eventual — AWS observa "um atraso de alguns segundos entre um download sendo gravado e a agregação sendo atualizada." Para painéis, tabelas de classificação e contadores de tendências, tudo bem.

Uma nova invocação do Lambda executa novamente o ADD, então "uma nova tentativa aumentaria a contagem mais de uma vez", deixando um aproximado valor. Para contagens exatas, adicione idempotência (por exemplo, uma expressão de condição digitada em o id do item de origem); caso contrário, a pequena margem é adequada para análises e tabelas de classificação.

Padrão 3: agrupamento do lado do aplicativo após Scan/Query

Ou leia os itens e agrupe-os em seu código.

groups = {}
resp = table.scan()                        # or query() for one partition
while True:
    for item in resp["Items"]:
        key = item["status"]
        groups[key] = groups.get(key, 0) + 1
    if "LastEvaluatedKey" not in resp:
        break
    resp = table.scan(ExclusiveStartKey=resp["LastEvaluatedKey"])

Isso é correto e às vezes a decisão certa – mas seja honesto sobre o custo. Um Scantodos os itens da tabela, e a capacidade de leitura é a mesma se você filtra ou não. Portanto, agrupar o lado do aplicativo em um Scan completo significa que você paga para ler a tabela inteira em cada agregação, e a latência aumenta com a tabela. AWS lista "digitalizar e contar em tempo de leitura" como "adequado apenas para conjuntos de dados onde a latência não é uma preocupação" (AWS: Por que agregações de pré-cálculo).

Com escopo reduzido para uma única partição via Query (por exemplo, contar os pedidos para um cliente), o agrupamento no lado do aplicativo é perfeitamente razoável - você está lendo apenas um coleção de itens. Para a diferença total de custos entre os dois, consulte Query vs Scan. Em us-east-1 sob demanda, uma tabela completa Scan faturas 0,5 RCU por 4 KB eventualmente consistente para cada item examinado — uma tabela de 1 GB de linhas de 1 KB equivale a aproximadamente 250.000 RCU antes do seu aplicativo agrupa qualquer coisa. Dimensione um item representativo com o calculadora de tamanho de item e, em seguida, avalie a linha digitalize na calculadora de preços.

Para análise genuinamente ad-hoc SQL sobre uma mesa DynamoDB - o descartável "GROUP BY status, conte-os" você executaria uma vez - a resposta de AWS é apontar um mecanismo separado: o conector Amazon Athena DynamoDB permite consultar a tabela com SQL real (GROUP BY, agrega e até JOINs para outras fontes) através de um conector Lambda (AWS: conector Amazon Athena DynamoDB). Ele verifica a tabela nos bastidores, portanto é uma ferramenta de relatórios/BI, não um caminho ativo.

Qual padrão eu uso?

Você precisa…Usar
Um total de grupo conhecido em um caminho de leitura dinâmicaPadrão 1 — contador atômico (ADD)
Agrega sem tocar no caminho de gravaçãoPadrão 2 — Streams + rollup Lambda
Uma contagem com escopo para uma partiçãoPadrão 3 — Query e agrupe no aplicativo
Totais exatos, sem desviosPadrão 1/2 com proteção de idempotência
Um GROUP BY único durante a exploraçãoDynoTable Workbench (abaixo) ou Atenas
BI/relatórios recorrentes com SQLConector Athena DynamoDB

Executando GROUP BY diretamente no DynoTable do SQL Workbench

Os padrões acima mostram como você atende agregados na produção. Mas quando você está explorando uma tabela - "quantos pedidos por status, agora?" - você não quer provisionar um Lambda ou stand up Athena. Você deseja digitar a consulta.

É para isso que serve o SQL Workbench de DynoTable. Funciona real SQL — GROUP BY, COUNT, SUM, AVG, HAVING, até mesmo JOIN — diretamente contra o seu tabelas DynamoDB ativas, executando a agregação do lado do cliente nas linhas lê. É o SQL que o endpoint PartiQL de DynamoDB rejeita:

SELECT status, COUNT(*) AS orders, SUM(total) AS revenue
FROM "Orders"
GROUP BY status
HAVING SUM(total) > 1000
ORDER BY revenue DESC

Sob o capô DynoTable lê itens da maneira que API permite (Query onde pode, Scan onde deve), materializa-os e faz o agrupamento no Workbench - a mesma mecânica de "ler e agregar" do Padrão 3, apenas sem o loop e dentro das regras de padrão de acesso do DynamoDB. É construído para exploração e análise ad hoc, não para substituir uma produção rollup em um caminho de leitura quente. Para isso, pré-calcule (Padrão 1/2).

Para o lado JOIN da mesma fatia — DynoTable executa junções entre tabelas PartiQL também não pode - consulte DynamoDB JOIN. Comparando GUI clientes exatamente com esse recurso? Veja a comparação da GUI DynamoDB.

FAQ

O DynamoDB PartiQL suporta GROUP BY? Não. DynamoDB's PartiQL SELECT suporta apenas WHERE e ORDER BY - não GROUP BY, HAVING, funções agregadas ou JOIN. A gramática é documentado como SELECT… FROM… [WHERE…] [ORDER BY…].

Posso fazer COUNT(*) em uma tabela DynamoDB inteira? Não como uma função agregada — PartiQL não tem nenhuma. O API oferece Select=COUNT em Scan/Query, que retorna uma contagem de itens correspondentes mas ainda lê (e fatura) cada item que a digitalização toca (AWS Scan API referência: a capacidade é baseada em itens examinados, não devolvidos). Para uma leitura frequente total, mantenha um item contador (Padrão 1).

Posso GROUP BY a chave de partição? Não em DynamoDB ou PartiQL. Se "por chave de partição" for um padrão de acesso conhecido, mantenha um item agregado por chave com um ADD atômico (Padrão 1) ou role-o com Streams + Lambda (Padrão 2).

Como faço SUM ou AVG por grupo? SUM: mantém um total em execução por grupo e ADD a ele na gravação. AVG: armazenar tanto a soma quanto a contagem e divisão no momento da leitura - não há média nativa. Para um AVG exploratório único, execute-o no DynoTable do SQL Workbench ou através do Conector Athena DynamoDB.

Existe uma solução alternativa para partiql group by? Não há lado PartiQL. Pré-calcule o agregado (contadores/Streams) e SELECT o item de rollup ou execute GROUP BY em um mecanismo que tenha um - DynoTable's Workbench para ad-hoc, Athena para relatórios recorrentes.


Quer executar GROUP BY em suas próprias tabelas sem escrever um Lambda? Experimente DynoTable e aponte o SQL Workbench para uma mesa ao vivo.

Atualizado