Scan de DynamoDB en Python (boto3)

Un scan en boto3 son dos decisiones: la petición y cómo la paginas. El fragmento de abajo usa el paginador integrado, que no es un envoltorio que alguien haya escrito alrededor de tu bucle. Son cinco líneas de configuración de botocore, y esas cinco líneas deciden si tu scan es correcto y cuánto cuesta. (Si deberías estar haciendo scan siquiera es otra cuestión.)

Código

import boto3

client = boto3.client("dynamodb")

paginator = client.get_paginator("scan")

items = []
for page in paginator.paginate(
    TableName="Music",
    FilterExpression="#filter0 >= :filterValue0",
    ExpressionAttributeNames={"#filter0": "Year"},
    ExpressionAttributeValues={":filterValue0": {"N": "2010"}},
):
    items.extend(page["Items"])

print(f"Matched {len(items)} items")

Explicación

  • El paginador es datos, no código. botocore incluye una entrada por operación en paginators-1.json; la de Scan dice {"input_token": "ExclusiveStartKey", "output_token": "LastEvaluatedKey", "limit_key": "Limit", "result_key": ["Items", "Count", "ScannedCount"], "non_aggregate_keys": ["ConsumedCapacity"]}. Todo lo de abajo se desprende de esas claves.
  • PaginationConfig={"PageSize": n} fija Limit, porque Limit es la limit_key. Limit acota los Items leídos, nunca los devueltos, así que con una FilterExpression una página puede estar vacía y aun así tener coste.
  • MaxItems cuenta los Items de result_key y devuelve un NextToken que puedes pasar como StartingToken en un proceso posterior. No impide que la petición lea más allá de tu corte.
  • build_full_result() agrega solo los campos de result_key. Items, Count y ScannedCount se suman; ConsumedCapacity es una non_aggregate_key, así que el resultado combinado informa de la capacidad de una sola página como si fuera la del scan entero. Súmala tú, página a página, o subestimarás por un factor igual al número de páginas.
  • La FilterExpression corre en el servidor después de la lectura, así que se te factura por ScannedCount, no por Count. #filter0 pone alias a Year porque es una palabra reservada; sin el alias la petición falla antes de leer nada.
  • Todos los errores llegan como botocore.exceptions.ClientError. Ramifica según e.response["Error"]["Code"]; las clases por error existen solo como atributos generados sobre el cliente (client.exceptions.ProvisionedThroughputExceededException), nunca como símbolos importables.
  • La API de recursos es la otra ergonomía. Table.scan recibe tipos nativos de Python, devuelve los números como decimal.Decimal y construye filtros con Attr("Year").gte(2010) en vez de con mapas de marcadores.

Cuánto cuesta de verdad una página filtrada

60 Items de unos 2 KB cada uno, con Year = 2024 coincidiendo en dos de ellos, PageSize=10, ejecutado contra DynamoDB Local:

page 1: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 2: Count=1 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 3: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 4: Count=1 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 5: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 6: Count=0 ScannedCount=10 CU=2.5 LastEvaluatedKey=yes
page 7: Count=0 ScannedCount=0 CU=0.0 LastEvaluatedKey=no
total CU across pages: 15.0

Cuatro de las seis páginas reales no devolvieron nada, a precio completo. Esa es la forma del bug que el paginador existe para evitar: un bucle artesanal que corta cuando Items está vacío abandona en la página 1 e informa de cero cuando había dos canciones coincidentes.

La página 7 es la otra mitad. La página 6 alcanzó su Limit en el último Item de la tabla, así que DynamoDB devolvió un LastEvaluatedKey igualmente y el paginador gastó una ida y vuelta más para descubrir que no quedaba nada. Un LastEvaluatedKey significa «me detuve», no «hay más».

Llamar a build_full_result() sobre el mismo scan informa de CapacityUnits: 2.5. Las seis páginas consumieron 15,0.

Paginar sin escribir el bucle

El DynamoDB Query Builder ensambla el filtro, el mapa de alias y el bucle de paginación como un solo programa ejecutable, así que la trampa de Limit frente a Count de arriba queda resuelta antes de que lo pegues. Para paginar una tabla real de forma interactiva en vez de desde un script, descarga DynoTable.

Guías relacionadas

Referencias

Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.

Construye esta solicitud visualmente

Compón esta operación en el Generador de consultas de DynamoDB gratuito —condición de clave, filtro, índice, Limit, orden de clasificación y un bucle de paginación— y cópiala de vuelta como un programa ejecutable para SDK v3, CLI o boto3.

Abrir el Generador de consultas de DynamoDB

Trabaja con DynamoDB sin la Consola

Un cliente de escritorio rápido para DynamoDB que ejecuta el SQL real que DynamoDB no puede — JOINs, GROUP BY, agregaciones — con edición visual y un agente de IA con tus propias claves de Bedrock.

Prueba gratuita de 30 días, sin tarjeta — después, el plan Free sin límite de tiempo.