Python(boto3)での DynamoDB Query

このページが短い理由は boto3 の query ページネーターにあります。LastEvaluatedKey を完全に隠してくれるからです。ただし、おそらく欲しかったはずの数字も 1 つ隠します。信用する前に知っておく価値があるのはそこです。そもそも query に手を伸ばすべき場面については Query と Scan の比較を参照してください。

コード

import boto3

client = boto3.client("dynamodb")

paginator = client.get_paginator("query")

items = []
for page in paginator.paginate(
    TableName="Music",
    KeyConditionExpression="#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)",
    ExpressionAttributeNames={"#hashKey": "Artist", "#rangeKey": "SongTitle"},
    ExpressionAttributeValues={":hashKeyValue": {"S": "Arturo Sandoval"}, ":rangeKeyValue": {"S": "C"}},
):
    items.extend(page["Items"])

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

ページネーターは請求額を合計してくれません

1 曲あたり約 3.9 KB、すべてが Artist = "Arturo Sandoval" の下にある 600 曲のフィクスチャに対して、このループは 3 ページを返します。271 件、271 件、58 件で、それぞれ 128.5、128.5、27.5 読み取りユニットです。同じページネーターに 1 つにまとめた結果を求めると、こうなります。

build_full_result() -> Items 600  Count 600  ScannedCount 600
                       ConsumedCapacity.CapacityUnits 128.5

CountScannedCount は合計されました。ConsumedCapacity は合計されていません。これは最初のページの値であり、実際の合計は 284.5 でした。botocore の DynamoDB ページネーター設定はその理由を明示しています。CountScannedCount は結果キーとして、ConsumedCapacity は非集計キーとして列挙されています。build_full_result() からキャパシティをログに取っているなら、パーティション全体の読み取りを半分以上少なく報告していることになります。

上の for page in paginator.paginate(...) ループの中にあるページごとの dict は生のレスポンスなので、page["ConsumedCapacity"]["CapacityUnits"] を自分で合計すれば正直な 284.5 が得られます。

58 回の余分なラウンドトリップを生む Limit

Limitquery の有効なパラメーターなので paginate() は受け付けますが、Python ユーザーが期待するパラメーターではありません。

paginate(..., Limit=10)  ->  61 pages, 10 items each
paginate(...)            ->   3 pages

これは全体ではなく リクエストごと のアイテム数を制限するので、ページネーターは同じ 600 件を取得するために律儀に 61 回の HTTP 呼び出しを行います。総数を制限するには PaginationConfig={"MaxItems": 10} を使ってください。Limit に対応するつまみは PaginationConfig["PageSize"] です。

2026-07-28 に、CPython 3.14.6 上の boto3 1.43.58 で、DynamoDB Local(amazon/dynamodb-local)に対して計測しました。

解説

  • クライアントは双方向で DynamoDB JSON を話します。値は {"S": "Arturo Sandoval"} として入り、Year{"N": "1994"} として返ってきます。一方 リソース API(boto3.resource("dynamodb").Table(...).query)は両方向に変換し、Decimal('1994') を渡してきます。これは金額には正しく、float に足せないと拒否された初回は驚かされます。
  • Key("Artist").eq(...) はリソース API 専用です。クライアントに渡すとリクエストが出る前に例外になります。ParamValidationError: Invalid type for parameter KeyConditionExpression ... valid types: <class 'str'> です。クライアントが欲しいのは、このページが組み立てている式の文字列です。
  • キー条件は 1 つの等価条件に加えて、最大 1 つのソートキー比較です=<<=>>=BETWEENbegins_with)。それ以外は FilterExpression に入れてください。boto3 はそのまま渡し、DynamoDB は読み取りの後に適用します。ScanIndexForward=False で順序が逆になり、IndexName="..." でインデックスに向けられます。

ビジュアルに行う

DynamoDB Expression Builder は、キー条件と型付きの ExpressionAttributeValues マップを boto3 にそのまま渡せる Python として書き出します。ここは {"N": "2010"} の代わりに {"N": 2010} と打ってしまいがちな箇所です。

同じクエリをキー条件のフォームから自分のテーブルに向け、結果をページ送りできるグリッドで読むには、DynoTable をダウンロードしてください。

関連ガイド

参考資料

このリクエストをビジュアルに組み立てる

この操作を無料の DynamoDB クエリビルダーで組み立て — キー条件、フィルタ、インデックス、Limit、ソート順、ページネーションループ — 実行可能な SDK v3・CLI・boto3 のプログラムとしてコピーして戻れます。

DynamoDB クエリビルダーを開く

Console なしで DynamoDB を扱う

DynamoDB では実行できない本物の SQL(JOINs、GROUP BY、集計)を実行する高速な DynamoDB デスクトップクライアント。ビジュアル編集と、あなた自身の Bedrock キーで動く AI エージェントを備えています。

30日間無料トライアル、クレジットカード不要 — その後は期限のない Free プラン。