DuplicateItemException: duplicate primary key

TL;DR — PartiQL INSERT 是一次严格的创建:如果具有相同主键的项目已经存在,DynamoDB 会抛出 DuplicateItemException 而不是覆盖它。要修改现有项目,请使用 PartiQL UPDATE;要获得 PutItem 的"存在则替换"语义,请使用 PutItem 本身——PartiQL INSERT 特意从不替换。

含义

DuplicateItemException: There was an attempt to insert an item with the
same primary key as an item that already exists in the DynamoDB table.

PartiQL 数据平面(ExecuteStatement / ExecuteTransaction / BatchExecuteStatement)把 INSERT 映射为一次条件创建——只有当不存在具有该主键的项目时它才成功。这与原生 PutItem 的默认行为相反,后者会静默地替换一个已有的项目。如果你来自 SQL 并期望 INSERT 在键重复时失败,这正是那个行为;如果你期望的是 upsert 语义,那这个错误就是意外。

为什么会发生

  • 项目确实已经存在——一次重试、一次重放,或两个写入者在同一个键上竞速,都发出了 INSERT
  • 你想要的是更新,而非创建——把 PutItem 风格的代码移植到 PartiQL,并假定 INSERT 会替换。
  • 一个非幂等的重试循环——第一次尝试成功了但响应丢失了(超时),重试又重新插入了同一个键。
  • 一个并不唯一的合成键——你组合的分区键/排序键碰撞的频率比你想象的更高(例如一个秒级精度的时间戳)。

如何修复

  1. 在更新现有项目?使用 UPDATE

    UPDATE "orders" SET status = 'shipped' WHERE pk = 'ORDER#123' AND sk = 'META'
  2. 想要"存在则替换"(PutItem 语义)?调用 PutItem——PartiQL 没有 upsert 语句,而原生 API 的默认行为正是你想要的覆盖:

    await client.send(new PutItemCommand({TableName: 'orders', Item: item}));
  3. 当创建是幂等的时,把它当作成功——如果一次重试对你第一次尝试已经写入的项目遇到 DuplicateItemException,捕获并忽略它往往是正确的处理方式。

  4. 当你依赖唯一性时保留 INSERT——这个异常就是你的仅创建守卫,是 PutItem 条件上 attribute_not_exists() 的 PartiQL 等价物。

对着真实数据尝试语句是内化 INSERT/UPDATE 之分的最快方式——DynoTable 桌面应用在一个 SQL 工作台中对你的实时表运行 PartiQL,而当你需要 PutItem 语义时,DynamoDB Expression Builder 会发出等价的原生 API 请求。

相关错误

References

最后核实于 2026-07-13,依据上方链接的 AWS 官方文档。

无需控制台即可使用 DynamoDB

DynoTable 是面向 DynamoDB 的快速桌面客户端 —— 浏览表、运行 SQL 风格的查询,并在本地编辑项。