Débutant10 min de lecture

COUNT, SUM et agrégats dans DynamoDB

DynamoDB dispose d'exactement un agrégat intégré : compter les items correspondants avec Select=COUNT. Il n'y a pas de SUM, AVG, MIN ou MAX natif. Et même le compte que tu peux obtenir lit (et facture) chaque item qu'il a compté. Ce guide couvre ce qui est réellement pris en charge, les approximations vers lesquelles on se tourne, et comment exécuter de vrais COUNT/SUM/AVG sur une table quand tu en as besoin.

DynamoDB peut-il faire SUM, COUNT et des fonctions d'agrégation ?

Essentiellement non. Le seul agrégat intégré de DynamoDB est Select=COUNT, qui renvoie le nombre d'items correspondants mais lit (et facture) quand même chaque item. Il n'y a pas de SUM, AVG, MIN ou MAX natif, et PartiQL n'en ajoute aucun non plus. Pour de vrais agrégats avec GROUP BY, replie-les dans ton application, maintiens un compteur, ou exécute du SQL dans le Workbench de DynoTable.

  • Select=COUNT renvoie le nombre d'items correspondants, mais DynamoDB lit quand même chaque item pour le produire — tu paies le plein coût de lecture d'un Scan/Query, pas un coût de « comptage » bon marché.
  • Il n'y a pas de SUM, AVG, MIN ou MAX natif. Les opérations de lecture de DynamoDB renvoient des items ; elles ne les replient pas en un nombre. PartiQL n'ajoute pas d'agrégats non plus.
  • DescribeTable.ItemCount est gratuit mais approximatif et mis à jour seulement « environ toutes les six heures » — bien pour une tuile de tableau de bord, faux pour tout ce qui doit être exact.
  • Pour des COUNT/SUM/AVG/MIN/MAX exacts (avec GROUP BY), agrège dans ton application, maintiens un compteur, ou exécute-les dans le SQL Workbench de DynoTable (ci-dessous).

Compter des items : Select=COUNT

Query et Scan acceptent tous deux un paramètre Select. Règle-le sur COUNT et la réponse porte les comptes au lieu des items :

aws dynamodb scan \
  --table-name Orders \
  --select COUNT \
  --filter-expression "#s = :open" \
  --expression-attribute-names '{"#s":"status"}' \
  --expression-attribute-values '{":open":{"S":"OPEN"}}'

La réponse te donne deux nombres (AWS : Compter les items dans les résultats) :

  • Count — « le nombre d'items qui restent, après l'application d'une expression de filtre (si présente). »
  • ScannedCount — « le nombre d'items évalués, avant l'application d'un ScanFilter. » Sans filtre, ScannedCount est identique à Count.

Si tu n'as que la et que tu dois compter les doublons en son sein, la condition + le filtre que tu passes sont exactement ce que le Générateur d'expressions DynamoDB génère — la FilterExpression et les maps ExpressionAttributeNames/Values ci-dessus, plus la KeyConditionExpression quand tu comptes au sein d'une partition via Query — sans échapper le JSON à la main.

Deux autres pièges qui touchent ceux qui comptent de grandes tables :

  • La limite de page de 1 Mo s'applique toujours. « Si la taille de l'ensemble de résultats du Scan est supérieure à 1 Mo, ScannedCount et Count ne représentent qu'un comptage partiel du total des items » (documentation Scan d'AWS). Tu dois paginer en réinjectant le LastEvaluatedKey de chaque réponse comme ExclusiveStartKey de la requête suivante, en tenant un total courant pour obtenir le vrai nombre — la même boucle couverte dans la pagination DynamoDB.
  • Une Query étroite bat un Scan. Select=COUNT sur une Query ne métre que les items de la partition ciblée, pas toute la table. Si tu peux fixer une clé de partition (table de base ou GSI), compte là — c'est l'écart de coût Query-vs-Scan appliqué au comptage.

Select=COUNT vs ItemCount (et pourquoi il est périmé)

DescribeTable renvoie un ItemCount (et un TableSizeBytes) gratuitement, sans coût de lecture. Le hic est dans la référence d'API elle-même : « DynamoDB met à jour cette valeur environ toutes les six heures. Les changements récents pourraient ne pas être reflétés dans cette valeur. » Elle peut donc être bien en retard sur l'état réel de ta table.

Select=COUNTDescribeTable.ItemCount
ExactitudeExact (pour l'ensemble correspondant)Approximatif
FraîcheurEn directMis à jour ~toutes les 6 heures
CoûtLit + facture chaque item comptéGratuit (métadonnées)
Peut filtrer / compter un sous-ensembleOui (expression de filtre)Non — toute la table uniquement

Utilise ItemCount pour une estimation approximative « quelle est la taille de cette table » ou une tuile de tableau de bord. Utilise Select=COUNT quand tu as besoin d'un nombre exact, filtré ou courant — et accepte le coût de lecture. Pour quelque chose de vraiment en direct et gratuit, suis un compteur toi-même (voir Motifs d'agrégation ci-dessous).

Pourquoi il n'y a pas de SUM/AVG/MIN/MAX natif

Les opérations de lecture de DynamoDB renvoient des items. Il n'y a pas de planificateur de requêtes pour replier un ensemble de résultats en un scalaire, donc il n'y a rien pour calculer un SUM ou un AVG. Le comptage est le seul repli que l'API offre, via Select=COUNT.

PartiQL n'y change rien. La grammaire SELECT de PartiQL est SELECT {{expression}} [, …] FROM {{table}}[.{{index}}] [WHERE …] [ORDER BY {{key}} …], où l'expression est « une projection formée à partir du joker * ou d'une liste de projection d'un ou plusieurs noms d'attribut ou chemins de document. » Il n'y a pas de fonction d'agrégation ni de clause GROUP BY dans cette grammaire — et ORDER BY prend une {{key}}, documentée comme « une clé de hachage ou une clé de tri à utiliser pour ordonner les résultats renvoyés. » Chaque SELECT PartiQL se compile toujours en un GetItem, une Query ou un Scan, donc SELECT SUM(total) FROM "Orders" n'est tout simplement pas exprimable. (Plus sur le plafond de PartiQL dans PartiQL vs SQL.)

Motifs d'agrégation (compteurs, streams, côté application)

Puisque DynamoDB n'agrège pas pour toi, les motifs établis poussent le travail ailleurs :

  • Item compteur maintenu. Garde un item dédié (par ex. PK = "STATS#orders") et fais un ADD sur un attribut numérique à chaque écriture avec un UpdateItem. Lire l'agrégat n'est alors qu'un seul GetItem — exact et bon marché, mais tu es responsable de la logique d'incrémentation, de sa cohérence et de la contention si un compteur est martelé.
  • alimentant un agrégateur. Active un stream et relie-le à une Lambda qui met à jour des totaux courants (comptes, sommes) à mesure que les items changent. D'après la documentation Streams d'AWS, tu peux configurer le StreamViewType du stream pour que chaque enregistrement porte les NEW_AND_OLD_IMAGES — « à la fois la nouvelle et l'ancienne image de l'item » — de quoi garder les agrégats de type SUM à jour sans re-balayer. Les enregistrements de stream sont soumis à une durée de vie de 24 heures (« les enregistrements de stream d'un shard sont supprimés automatiquement après 24 heures »), donc le consommateur doit suivre le rythme.
  • Repli côté application. Parcours les items correspondants et accumule le SUM/AVG/MIN/MAX dans ton propre code. Correct, mais cela lit (et facture) chaque item à chaque fois — le même profil de coût que Select=COUNT, plus le transfert de données.
  • Décharger vers l'analytique. Pour une agrégation analytique lourde ou ad-hoc, exporte la table vers S3 et interroge-la avec Athena, ou envoie-la en flux dans un entrepôt. D'après la documentation export-vers-S3 d'AWS, l'export « ne consomme pas d'unités de capacité de lecture » et te permet d'« effectuer des analyses et des requêtes complexes à l'aide de services AWS tels qu'Athena » — la voie recommandée par AWS une fois que tu as dépassé l'agrégation par requête.

Chacun échange la simplicité contre soit une comptabilité au moment de l'écriture (compteurs, streams), soit un coût au moment de la lecture (scans côté application). Aucun motif ne fait calculer un SUM à DynamoDB lui-même gratuitement. La version groupée de ce compromis — agréger par clé plutôt que sur toute la table — a son propre guide : DynamoDB GROUP BY.

Exécuter COUNT/SUM/AVG dans le SQL Workbench de DynoTable

Quand tu as juste besoin de la réponse — « combien de commandes OPEN, et quel est leur total » — sans écrire une boucle de scan paginée ni une Lambda, le SQL Workbench de DynoTable exécute de vrais agrégats. Il matérialise tes tables à travers le runtime Query/Scan réel de DynamoDB, puis exécute un seul SELECT par-dessus — agrégats, GROUP BY, HAVING, DISTINCT : du SQL dans le respect des règles d'accès de 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 DESC

Cela fait COUNT, SUM, AVG, MIN, MAX, GROUP BY et ORDER BY sur un agrégat calculé — aucun de ceux-ci ne peut être exprimé par DynamoDB ni PartiQL (l'ORDER BY de PartiQL est limité aux attributs de clé) — en une seule instruction. C'est le même levier analytique que SQL pour DynamoDB ; pour l'histoire complète du groupement, voir DynamoDB GROUP BY.

Le Workbench est honnête sur le modèle d'accès sous-jacent, ce n'est pas un faux Postgres :

  • Les lignes passent toujours par le vrai Query/Scan de DynamoDB. Un GROUP BY sur toute une table reste un Scan en dessous — le Workbench fait remonter ce coût plutôt que de le cacher, le même compromis Query-vs-Scan.
  • Les agrégats s'exécutent sur les attributs scalaires matérialisés après l'atterrissage des lignes.

FAQ

Puis-je compter des items dans DynamoDB sans balayer ? Pas vraiment. Pour un compte exact et courant, tu dois lire les items — Select=COUNT métre quand même chaque item compté. Les seules options sans scan sont le DescribeTable.ItemCount approximatif (mis à jour ~toutes les 6 heures) ou un item compteur que tu maintiens toi-même à chaque écriture.

Comment compter des items par un GSI ? Exécute une Query (ou un Scan) sur l'index avec Select=COUNT. Compter via une partition de GSI étroite est bien moins coûteux que de balayer la table de base, car tu ne lis que les items de cette partition d'index — modélise l'index autour du compte dont tu as besoin.

DescribeTable.ItemCount est-il exact ? Il est approximatif. La référence d'API indique que DynamoDB met à jour ItemCount et TableSizeBytes « environ toutes les six heures », et que « les changements récents pourraient ne pas être reflétés dans cette valeur. » Ne l'utilise pas là où un nombre exact ou en direct compte.

DynamoDB peut-il faire SUM ou AVG ? Pas nativement, et pas en PartiQL — la grammaire SELECT de PartiQL n'a pas de fonctions d'agrégation. Agrège dans ton application, maintiens un compteur (éventuellement via DynamoDB Streams), ou exécute le SUM/AVG dans le SQL Workbench de DynoTable.

Quelle est la différence entre Count et ScannedCount ? ScannedCount est le nombre d'items que DynamoDB a évalués avant ton filtre ; Count est le nombre qui reste après. Ils sont égaux quand il n'y a pas d'expression de filtre. Un grand écart entre eux signale un comptage inefficace.


Besoin de sommer, moyenner ou grouper tes données DynamoDB sans écrire de boucle de scan ? Télécharge DynoTable et exécute-le dans un onglet Workbench. Tu compares d'abord les clients ? Vois où il se situe face à une interface graphique DynamoDB classique.

Mis à jour