入門閱讀時間 5 分鐘

如何把 DynamoDB 資料表匯出成 CSV(4 種方式)

DynamoDB 沒有原生的「匯出成 CSV」按鈕。每個值回來時都包在 DynamoDB 的 marshalled JSON 裡 — {"S": "..."}{"N": "123"}{"M": {...}} — 而一張資料表可以裝巢狀的 map、list 與 set,沒有顯而易見的扁平欄位表示法。所以「把 DynamoDB 匯出成 CSV」其實 是兩個問題:把項目弄出來,然後把帶型別的 JSON 攤平成 資料列。主控台與受管理的匯出都不會替你做第二步。

本指南依資料表大小、以及你的項目帶有多少巢狀,把四種做法排序。

如何把 DynamoDB 資料表匯出成 CSV?

DynamoDB 沒有原生的 CSV 匯出功能,因此你需要掃描或快照資料表,再把帶型別的 JSON 攤平成資料列。小型資料表可用 AWS CLI scan + jq 或簡短腳本;大型資料表則匯出到 S3 再轉換;若要篩選過、現成的 CSV,可使用 DynoTable 等 GUI 工具。

  • 小型資料表、臨時: AWS CLI scan + jq,或一個 20 行的腳本 (方法 1 / 方法 3)。 在巢狀屬性出現之前都還行。
  • 大型資料表(GB 以上): 匯出到 S3方法 2),然後轉換那份傾印。 它非同步執行且不消耗讀取容量 — 但它輸出 DynamoDB JSON,不是 CSV。
  • 過濾過/塑形過的 CSV(部分欄位、只取部分項目): GUI 匯出或 一個腳本。受管理的 S3 匯出給你的是整張資料表、未過濾。

方法 1:AWS CLI scan + jq

對小型資料表,你可以掃描它並用 jq 重塑輸出。一個 Scan 會讀 資料表中的每一個項目,並以最多 1 MB 的分頁回傳;CLI 會自動替你跟著分頁 (AWS 文件:掃描資料表)。

aws dynamodb scan --table-name MyTable --output json \
  | jq -r '.Items[] | [.id.S, .name.S, .price.N] | @csv' \
  > out.csv

陷阱就在那一行 jq:你得手寫 .id.S.name.S.price.N — 伸手穿過每個屬性的型別描述子SNBBOOLMLSSNSBS)去取得原始值。對一張帶三個 字串欄位的扁平資料表這還能管理。但一旦你有以下情況就崩潰了:

  • 巢狀 map/list{"M": {...}}{"L": [...]} 沒有單一欄位能 攤平進去;@csv 會卡住,或你得手動把儲存格做 JSON 編碼。
  • set{"SS": ["a","b"]} 是陣列,不是純量。
  • 稀疏屬性 — DynamoDB 是無 schema 的,所以項目 A 可能有 price, 項目 B 可能沒有。你固定的欄位清單會靜默地丟掉或錯位欄位。

也沒有一個懂 DynamoDB 型別的 --output csv。CLI 的 csv 輸出會把原始回應攤平,連描述子一併 — 所以你仍需要 jq(或 一個腳本)來剝掉型別標記。那就是「用 AWS CLI 把 DynamoDB 資料表匯出成 CSV」在超出最簡情況後永遠不是一行式的核心原因。

要用這種方式匯出一整張較大的資料表又不讓它花上一整天,可以用 --segment / --total-segments 把掃描平行化 (AWS 文件:平行掃描 — DynamoDB「藉由對每個項目的分割鍵套用雜湊函式來把項目指派到區段」, 因此區段大小可能不均),並閱讀 分頁,這樣你才不會停在第一個 1 MB 分頁。

方法 2:匯出到 S3(大型資料表)

對於任何具規模的資料表,受管理的匯出到 Amazon S3 是對的工具。 它從你時間點還原(PITR)視窗中的任一點匯出快照 — 所以資料表必須先啟用 PITR — 它非同步執行, 且不消耗讀取容量單位,因此對你資料表的 輸送量或可用性零影響(AWS 文件: 「匯出是非同步的,不會消耗讀取容量單位(RCU),且對資料表 效能與可用性無影響」;「你需要在資料表上啟用 PITR 才能使用匯出 功能」)。這也是主控台匯出到 S3 動作底層所 觸發的:主控台只是同一個 API 的前端, 所以它帶著同樣的 PITR 要求與同樣的 JSON 輸出。

aws dynamodb export-table-to-point-in-time \
  --table-arn arn:aws:dynamodb:us-east-1:123456789012:table/MyTable \
  --s3-bucket my-export-bucket \
  --export-format DYNAMODB_JSON

唯一的陷阱:S3 匯出不輸出 CSV。 它只寫 DynamoDB JSONAmazon Ion,以 gzip 過的檔案、採 JSON-lines 格式(每行一個項目),外加 manifest 檔案(AWS 文件:匯出輸出格式 — 資料檔以 .json.gz 寫出,「格式是 JSON lines」,伴隨 manifest-summary.json / manifest-files.json)。之後你仍需要一個轉換步驟:

  • Athena / Glue 能直接讀取匯出的 DynamoDB JSON — 把一張資料表指向 S3 前綴,然後從一個 SELECT 寫出 CSV(這是常見的「把 DynamoDB 匯出到 S3 再到 CSV」管線)。AWS 指出「許多 AWS 服務,例如 Athena 與 AWS Glue,會自動解析這個格式」 (匯出輸出格式)。
  • 自己動手做 — 解壓縮那些 .gz 檔、解析每行 JSON,並把它 攤平(與其他每種方法相同的攤平問題)。

它也是整張資料表的快照:沒有伺服器端過濾能只匯出 部分項目。如果你需要子集,要嘛事後在 Athena 中過濾,要嘛 改用腳本/GUI。

方法 3:一個快速腳本(boto3 / Node)

當你需要塑形過的 CSV — 特定欄位、過濾過的子集、對巢狀欄位的 自訂處理 — 一個小腳本勝過跟 jq 搏鬥。好處是 AWS SDK 會替你unmarshall 帶型別的 JSON:boto3 的 resource 介面與 JS SDK 的 DynamoDBDocumentClient 回傳純粹的 {"price": 2000} 而非 {"price": {"N": "2000"}}(boto3 的 resource 介面讓「資料型別 變為隱含」,依據 AWS Python 指南; JS DocumentClient「把帶註解的回應資料轉換成原生 JavaScript 型別」, 依據 @aws-sdk/lib-dynamodb)。

import boto3, csv

table = boto3.resource("dynamodb").Table("MyTable")
rows, resp = [], table.scan()
rows += resp["Items"]
while "LastEvaluatedKey" in resp:                  # 分頁到底
    resp = table.scan(ExclusiveStartKey=resp["LastEvaluatedKey"])
    rows += resp["Items"]

with open("out.csv", "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=["id", "name", "price"])
    w.writeheader()
    for r in rows:
        w.writerow({k: r.get(k) for k in w.fieldnames})

你仍然得自己做兩個 SDK 替你做不了的決定:要如何把巢狀 map/list 攤平成欄位(把儲存格做 JSON 編碼?用點路徑展開鍵?),以及對 稀疏屬性要怎麼處理(這裡缺鍵透過 r.get(k) 變成空儲存格)。 而且別漏掉 LastEvaluatedKey 迴圈 — 單一 scan() 呼叫只回傳 第一個 1 MB 分頁,所以少了它你會靜默地只匯出資料表的一部分。

與方法 1 相同的告誡:這裡的整表 scan 仍會消耗讀取容量並 與即時流量爭用。對於大型資料表,請優先用 方法 2 並重塑那份傾印。

方法 4:在 DynoTable 中一鍵匯出

腳本與 CLI 路線可行,但你每次都重建同樣的攤平與分頁 邏輯。DynoTable 替你做:執行或過濾一個查詢, 然後把可見的資料列直接匯出成 CSV(或 Excel) — 型別描述子 解開、巢狀 map 與 list 攤平、set 處理好,輸出中只有你 實際想要的項目與欄位。

因為你匯出的是目前的視圖,你得到 方法 2 的整表快照給不了的、過濾過/塑形過的 CSV — 而且不用寫掃描迴圈。它是一款桌面版 DynamoDB 用戶端,就是你 已經用來瀏覽資料表的那個工具;看看它與其他 DynamoDB GUI 相比如何。

陷阱:DynamoDB JSON 與扁平 CSV

不論你挑哪種方法,DynamoDB 資料模型與扁平 CSV 之間同樣的 那幾個不匹配都會咬到你:

  • 型別描述子。 原始 API / CLI / S3 匯出的輸出會包住每個值 ({"S": "..."}{"N": "123"})。你要嘛透過 SDK 解開它,要嘛自己 剝掉描述子。完整集合是 SNBBOOLNULLMLSSNSBS — 請見 DynamoDB 資料型別
  • 巢狀 map 與 listML)可嵌套至 32 層深AWS 文件:資料型別 — list 與 map「可彼此巢狀,以表示複雜的資料結構, 最多 32 層深」),且沒有自然的單一欄位形式。請事先決定: 把儲存格做 JSON 編碼,或把巢狀鍵展開成點路徑欄位(address.city)。
  • setSS/NS/BS)是無序的集合,不是純量 — AWS 警告 「set 內值的順序不會被保留」 (資料型別) — 所以攤平成有分隔符的字串,並別依賴元素順序。
  • 稀疏屬性。 DynamoDB 是無 schema 的,所以兩個項目可以有不同的 屬性。沒有固定的欄位集;請把跨所有項目的鍵聯集起來,否則 欄位會錯位。這是 單表設計的直接後果,那裡一張資料表裝著數種 實體形狀。
  • 分頁。 Scan(與 Query)每次呼叫最多回傳 1 MB。如果你不 在 LastEvaluatedKey 上迴圈,你會靜默地只匯出第一個分頁。請見 分頁
  • 數字精度。 DynamoDB 的數字帶有最多 38 位精度,且 以字串傳輸(AWS 文件:資料型別: 「數字最多可有 38 位精度」;「所有數字都以字串形式透過 網路傳送到 DynamoDB」);試算表軟體可能把長數字或 ID 強制轉成浮點數而丟失位數。請把它們保留為文字。

常見問題

我要如何用 AWS CLI 把 DynamoDB 資料表匯出成 CSV? 掃描資料表並用 jq 重塑輸出(方法 1):aws dynamodb scanjq 剝掉每個值的型別描述子 → @csv。沒有懂 DynamoDB 的 --output csv,所以你永遠自己做型別剝除,而且它在巢狀 map、list 與 set 上會崩潰。

我能從 AWS 直接把 DynamoDB 資料表匯出成 CSV 嗎? 不能一步到位。主控台與受管理的 S3 匯出都產生 DynamoDB JSON 或 Amazon Ion,從不是 CSV。你永遠需要一個轉換步驟 — CLI + jq、一個腳本、 對 S3 傾印用 Athena/Glue,或一個替你做攤平的 GUI。

我要如何在不影響生產的情況下匯出整張 DynamoDB 資料表? 使用匯出到 S3 功能(方法 2)。它非同步執行且消耗 沒有讀取容量單位,所以它不會與即時流量爭用 — 不像 Scan,後者是依你資料表的輸送量計費 (AWS 文件)。 它要求啟用 PITR,並匯出整張資料表、而非過濾過的子集。

我要如何把 DynamoDB 匯出到 S3 成為 CSV? 受管理的匯出只把 DynamoDB JSON / Ion 寫到 S3,所以「成為 CSV」是第二 步:把匯出前綴註冊成一張 Athena(或 Glue)資料表,再從一個 SELECT 寫出 CSV。沒有 --export-format CSV

我要如何把 DynamoDB 匯出到 Excel? 先匯出成 CSV(上面任一方法),再在 Excel 中開啟那份 CSV — 把長的 數值 ID 保留為文字,這樣它們才不會被轉成浮點數。沒有從 DynamoDB 直接 .xlsx 匯出的方式;像 DynoTable 這類 GUI 能把目前的視圖 直接存成可給試算表用的 CSV。

為什麼我匯出的 JSON 到處都是 {"S": ...}{"N": ...} 那是 DynamoDB 的 marshalled 線路格式 — 每個值都被標上一個型別 描述子。請在寫 CSV 前用 SDK、 DynamoDB JSON 轉換器或一個 GUI 把它 unmarshall。 不論資料來自 API、CLI 還是 S3 匯出,線路格式都相同。

DynoTable 瀏覽、過濾你自己的資料表並匯出成 CSV,或 先在 JSON 轉換器中解開一份 DynamoDB JSON 樣本。

已更新