Python(boto3)中的 DynamoDB UpdateItem

boto3 給了你兩個 client 來做這次呼叫,而它們對「數字是什麼」意見不合。下面這個低階 client 收發的是 DynamoDB JSON,其中每個數字都是加了引號的字串。resource("dynamodb").Table(...) 收的是原生 Python 物件,直接拒絕 float,並把數字以 decimal.Decimal 交還給你。挑哪一個,才是這一頁真正的決定。

程式碼

import boto3

client = boto3.client("dynamodb")

response = client.update_item(
    TableName="Music",
    Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
    UpdateExpression="SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2",
    ExpressionAttributeNames={"#upd0": "Genre", "#upd1": "Year", "#upd2": "Awards"},
    ExpressionAttributeValues={":updValue0": {"S": "Latin Jazz"}, ":updValue1": {"N": "1994"}, ":updValue2": {"N": "1"}},
    ReturnValues="ALL_NEW",
)

print(response["Attributes"])  # the item after the update

說明

  • 子句文法不關 boto3 的事UpdateExpression 是一個它原封轉送的不透明字串;只有 DynamoDB 會去解析它,所以寫錯的代價是一次來回。這裡的 ADD 是消除讀取-修改-寫入競賽的不可分割遞增,在 ConditionExpression 裡放 attribute_exists(Artist) 會把 upsert 變成「只更新」,其餘的都在更新運算式裡。
  • 回應剛好有兩個最上層的鍵AttributesResponseMetadata。沒有狀態欄位可以檢查,也沒有列數。只要呼叫回來了,它就成功了;ResponseMetadata 帶著你想寫進記錄行裡的 RequestIdHTTPStatusCode
  • ReturnValues="UPDATED_NEW" 是省吃儉用的選項。它只回傳運算式碰過的屬性,在大型項目上,這就是「讀一個計數器」與「把整筆記錄運回來」之間的差別。
  • 錯誤是以 botocore.exceptions.ClientError 抵達的,你依 e.response["Error"]["Code"] 分支。少了別名會產生 ValidationException,訊息是 Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year。型別化的子類別確實存在,但只以 botocore 在 client 實例上動態產生的屬性形式存在(client.exceptions.ConditionalCheckFailedException),絕不會是可匯入的符號,所以一個作用域裡沒有 client 的輔助函式,只能用代碼字串。

Decimal 或 DynamoDB JSON,挑一個

resource API 會在請求被組出來之前就拒絕 float,訊息還明確告訴你它要什麼:

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

那是 boto3 自己的型別檢查,不是 DynamoDB 的。透過 resource API 存進 Decimal("4.5"),再用兩個 client 各讀回同一個屬性,你會得到:

resource Rating: Decimal('4.5') Awards: Decimal('2')
client Rating: {'N': '4.5'} Awards: {'N': '2'}

兩者都沒錯;它們是不同的契約。Decimal 保住了 DynamoDB 實際儲存的精度,也逼你去想清楚算術,代價是 Decimal("1") * 2 會出現在原本預期看到 int 的程式碼裡。低階 client 給你字串,把解析留給你,也就是上面那段程式碼在做的事。

由此得出的規則是:不要在同一條程式碼路徑裡混用它們。一個透過 Table.put_item 寫入、再透過 client.get_item 讀取的項目,回來的形狀是不一樣的,而這個臭蟲會在你測得比較少的那一條分支上浮現。

關於 TTL 屬性的一點提醒

Python 程式碼庫裡最常見的數值 SET 就是 TTL:SET expires_at = :t 搭配一個 Unix epoch。DynamoDB 把那個屬性讀成。改寫成 int(time.time() * 1000),值就是 1785269450912,以秒來看會落在西元 58542 年,於是項目永遠不會被刪除,也沒有任何東西抱怨。DynamoDB TTL 轉換器會用兩種單位把 epoch 都讀一遍,並告訴你你寫的是哪一種。之後想從真實資料表把存進去的值讀回來,請下載 DynoTable

相關指南

參考資料

最後驗證於 2026-07-28,對照上方連結的 AWS 官方文件。

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

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

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