Python(boto3)中的 DynamoDB GetItem

get_item 會依完整主索引鍵取回一個項目。boto3 的低階 clientboto3.client("dynamodb"))雙向說的都是 DynamoDB JSON,所以索引鍵要包著型別送進去,項目也會以同樣的形式回來。它與 queryscan 的差別,在以項目為單位的動作中說明。

程式碼

import boto3

client = boto3.client("dynamodb")

response = client.get_item(
    TableName="Music",
    Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
    ProjectionExpression="#proj0, #proj1, #proj2, #proj3",
    ExpressionAttributeNames={"#proj0": "Artist", "#proj1": "SongTitle", "#proj2": "AlbumTitle", "#proj3": "Year"},
)

item = response.get("Item")
if item is None:
    print("Item not found")
else:
    print(item)

說明

沒命中時,回應裡根本不會有 Item 這個鍵。不是 None,也不是空字典。用一個不存在的索引鍵讀同一張資料表,回應的最上層鍵剛好就只有:

['ResponseMetadata']

這就是程式碼用 response.get("Item") 的原因。response["Item"] 會在再普通不過的「找不到」路徑上丟出 KeyError,而這正是一列不存在的資料在 web handler 裡變成 500 的過程。這次讀取你還是要付費:AWS 的讀取容量頁面寫著 "if you perform a read operation on an item that doesn't exist, DynamoDB will still consume read throughput as outlined above"(2026-07-28 取得)。

Year 是保留字,這也是產生出來的程式碼為每一個投影屬性都加別名的原因。把 #proj 別名拿掉、改傳 ProjectionExpression="Year",引擎就會回絕這次讀取:

ValidationException: Invalid ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: Year

無條件加別名不花你什麼,卻能消掉整整一類失敗。完整清單共 573 個字;請見「Attribute name is a reserved keyword」

四種把 Key 寫錯的方式,三種不同的訊息。它們值得分辨清楚,因為沒有一個是大家預期的那句「provided key element does not match the schema」。在一張以 Artist(分割區)加 SongTitle(排序)為索引鍵的 Music 資料表上重現:

你傳了什麼逐字原文的 ValidationException 訊息
{"Artist": …} — 少了排序索引鍵The number of conditions on the keys is invalid
{"Artist": …, "SongTitle": …, "Extra": …}The number of conditions on the keys is invalid
{"Artist": …, "Song": …} — 屬性名稱錯了One of the required keys was not given a value
{"Artist": {"N": "1"}, …} — 型別錯了One or more parameter values were invalid: Type mismatch for key

請注意:少給一個索引鍵屬性和多給一個,產生的是同一則訊息,所以「number of conditions」的意思是「你交給我的不是剛好那份索引鍵結構」,而不是「你給得太少」。

ProjectionExpression 砍的是酬載,不是帳單。用 ReturnConsumedCapacity="TOTAL" 以三種方式讀同一個約 15 KB 的項目:

full item, eventually consistent          CapacityUnits: 2.0
ProjectionExpression="#y" (Year only)     CapacityUnits: 2.0
ProjectionExpression="#y" + ConsistentRead  CapacityUnits: 4.0

投影把回應從約 15 KB 變成一個數字,而對成本的改變是零。AWS 說得很白:"The number of capacity units consumed will be the same whether you request all of the attributes (the default behavior) or just some of them (using a projection expression)"(Query API Reference,2026-07-28 取得)。ConsistentRead=True 是那份清單上唯一會動到這個數字的旗標,而且它會讓數字翻倍。投影到底是拿來做什麼的,請見投影運算式

resource API 是另一份契約,不是比較好看的寫法boto3.resource("dynamodb").Table("Music").get_item(...) 回傳純 Python,而且每個數字都是 decimal.Decimal

{'Artist': 'Arturo Sandoval', 'AlbumTitle': 'Danzon', 'Awards': Decimal('0'), 'Year': Decimal('1994'), 'SongTitle': 'Cubano Chant'}

這是把兩面刃。用同一套 API 拿 float 寫回去,請求還沒離開你的機器就會先炸:

TypeError: Float types are not supported. Use Decimal types instead.

如果你被這個咬到,「Float types are not supported」有解法。真正的陷阱是在同一份程式碼庫裡混用這兩套 API:低階 client 會很樂意接受 resource API 早就回絕掉的 {"N": "1.5"}

錯誤是以 botocore 例外抵達的,而 boto3 給了它們真正的類別。在 1.43.58 上,條件失敗時丟出的物件是 ConditionalCheckFailedException,它是 ClientError 的子類別,所以 except ClientError 加上檢查 err.response["Error"]["Code"],以及 except client.exceptions.ConditionalCheckFailedException,兩種寫法都可行。你的程式碼庫已經在用哪一種就用哪一種;不要拿 str(e) 來比對。

改用視覺化操作

在你動手加別名之前:免費的 DynamoDB 保留字檢查器會收下你的屬性名稱,告訴你撞到 573 個保留字裡的哪幾個,並產出可直接貼上的 ExpressionAttributeNames 對應表。

想瀏覽資料表、對你自己的資料執行 GetItem — 索引鍵表單、結果網格、把請求複製回去變成 boto3 程式碼 — 請下載 DynoTable

相關指南

參考資料

已於 2026-07-28 以 boto3 1.43.58/botocore 1.43.58,對照連接埠 9000 上的 DynamoDB Local(amazon/dynamodb-local)重現。上方每一則訊息與每一個容量數字都是引擎輸出,逐字照錄。DynamoDB Local 不是線上服務;已知兩者措辭不同的地方,我們會在對應的錯誤頁上說明。

不必透過主控台就能操作 DynamoDB

一款快速的 DynamoDB 桌面用戶端,可執行 DynamoDB 無法執行的真正 SQL — JOINs、GROUP BY、聚合 — 並支援視覺化編輯與使用你自己的 Bedrock 金鑰的 AI 代理。

30 天免費試用,無需信用卡 — 之後為無時間限制的免費方案。