DynamoDB PartiQL 与 SQL:差异何在(以及哪些会失效)
DynamoDB PartiQL 最大的困惑来源——无论是对人还是对 AI 助手——都是把它当成关系型 SQL。它不是。PartiQL 是覆盖在 DynamoDB 现有操作之上的一层兼容 SQL 的接口,而不是一个能够联接、分组或聚合的查询引擎。那些熟悉的关键字背后藏着一台截然不同的机器。
心智模型
每一条 PartiQL 语句最终都会编译为 DynamoDB 的某个原生操作:
| 你写的内容 | DynamoDB 实际运行 |
|---|---|
SELECT … WHERE PK = … | GetItem 或 Query |
SELECT … (no PK) | Scan(读取整张表) |
INSERT INTO … | PutItem |
UPDATE … WHERE PK=… AND SK=… | UpdateItem(单个 item) |
DELETE … WHERE PK=… AND SK=… | DeleteItem(单个 item) |
没有任何查询规划器能从两张表中读取、构建哈希联接,或把若干行折叠成一个 COUNT。如果某个操作无法映射到单个 Get/Query/Scan/Put/Update/Delete,PartiQL 就根本无法表达它。整个故事就这么简单——下面的一切都是这一个事实的推论。
同样的映射,以流程图呈现——WHERE 子句决定一个 SELECT 是一次廉价的 Query 还是一次全表 Scan:
每条语句都恰好解析为一个原生操作——正是这种一对一的映射,让 PartiQL 无法联接、分组或聚合。
差异何在——逐项对比
下面标注了每一个 DynoTable 的 SQL Workbench 能够运行的 。Workbench 通过 DynamoDB 真实的查询运行时把你的表物化出来,然后在其上运行真正的 SQL——在 DynamoDB 访问模式规则之内的 SQL。
| Feature | Standard SQL | DynamoDB PartiQL | DynoTable Workbench |
|---|---|---|---|
JOIN … ON … | INNER / LEFT(联接到 PK 或 GSI 分区键) | ||
RIGHT / FULL / CROSS / comma-join | |||
| Self-join | (暂不支持) | ||
| Subqueries / derived tables | |||
CTEs (WITH …) | |||
UNION / INTERSECT / EXCEPT | |||
GROUP BY / HAVING | |||
Aggregates (COUNT/SUM/AVG/MIN/MAX) | |||
DISTINCT | |||
CASE / CAST | |||
| Window functions | |||
ORDER BY | 任意列 | 仅排序键(需要分区键 WHERE) | 任意列 |
LIMIT | 内联(改用请求的 limit 参数) | ||
LIKE | (改用 contains / begins_with) | ||
IS NULL / IS NOT NULL | (改用 attribute_not_exists / attribute_exists) | ||
SELECT * without a PK | 扫描 | 静默的全表 Scan | (带成本可见性) |
哪些会失效,以及为什么
下面这些是 DynoTable 的 PartiQL 校验器在查询发出之前就会标记的失败——每一条都可以追溯到一个真实的 DynamoDB 约束。
- 没有分区键的
SELECT *是一次隐藏的Scan。 PartiQL 不会报错;它只是读取每一个 item 再事后过滤,这正是友好语法背后那个经典的 Query 与 Scan 成本陷阱。 UPDATE/DELETE需要完整的主键。 它们映射到单个 item 的UpdateItem/DeleteItem,所以WHERE必须锁定分区键(在复合键表上还要锁定排序键)。你无法用一条语句“更新所有 status = 'open' 的行”。- 双引号是标识符,不是字符串。 DynamoDB PartiQL 在这一点上遵循 SQL 标准:
"name"是列名/表名,'name'是字符串值。用双引号包裹一个值是最常见的新手错误——校验器的提示信息原文就是 “双引号在 DynamoDB PartiQL 中界定标识符,而不是字符串。字符串值请使用单引号。” IN用方括号,不用圆括号:WHERE pk IN ['a','b'],上限为 50 个 PK 值 / 100 个非键值。- 没有
JOIN,没有聚合。 没有引擎能合并表或折叠行。这就是单表设计的权衡:你要预先围绕访问模式建模,因为查询层无法在事后重塑数据。
为什么 AI 助手会搞错
LLM 是在海量的关系型 SQL 上训练出来的,所以它们会自信地针对 DynamoDB 生成 JOIN、GROUP BY、LIKE、内联 LIMIT 以及双引号字符串字面量——而这些 DynamoDB 全都拒绝。DynoTable 自带的 model-query 自动修复正是为此而生,因为廉价模型会稳定地产出这些模式:它会剥离双重转义的引号,把 LIKE '%x%' → contains、IS NULL → attribute_not_exists 重写,并把内联 LIMIT 提升为请求参数。如果你的 AI 生成的“PartiQL”读起来像 Postgres,那就是破绽。
DynoTable 的 SQL Workbench:PartiQL 跑不了的查询
当你确实需要一个 JOIN 或一个 GROUP BY 时,DynoTable 的 SQL Workbench 就是答案。它会针对分区键校验每个 JOIN 的目标侧,通过 DynamoDB 真实的 Query/Scan 运行时把联接后的行物化出来,然后在其上运行你完整的 SQL(聚合、GROUP BY、DISTINCT、CASE、CAST)——在 DynamoDB 访问模式规则之内的 SQL。
-- Runs in the DynoTable Workbench (NOT in PartiQL):
SELECT c.country, COUNT(*) AS orders, SUM(o.total) AS revenue
FROM orders o
INNER JOIN customers c ON o.customerId = c.PK
GROUP BY c.country
ORDER BY revenue DESC诚实的约束(Workbench 强制执行 DynamoDB 的访问模型,它并不假装自己是 Postgres):
- 仅支持
INNER JOIN和LEFT JOIN——ON的目标属性必须是分区键或 GSI 分区键。不支持RIGHT/FULL/CROSS/ comma-join。 - 暂不支持自联接,不支持子查询,不支持派生表,不支持窗口函数。
- 联接和投影作用于标量属性。
如果你只需要为原始 API 组合条件和键表达式,DynamoDB Expression Builder 会生成正确的 FilterExpression / KeyConditionExpression,完全不经过 PartiQL 这层接口。要把 PartiQL 用对,参见配套的 PartiQL 示例;要估算任意查询的成本,使用 item 大小计算器。请注意,PartiQL 从不改变线上格式——值仍然以 DynamoDB-JSON 的形式传输。在选客户端?看看 Workbench 与普通 DynamoDB GUI 或 Dynobase 相比表现如何。
常见问题
PartiQL 和 SQL 一样吗?
不一样。PartiQL 是一种兼容 SQL 的查询语言,但在 DynamoDB 上它只暴露那些能映射到单个 Get/Query/Scan/Put/Update/Delete 的操作。它没有联接、聚合、子查询或 GROUP BY。
DynamoDB PartiQL 能做 JOIN 吗?
不能。DynamoDB PartiQL 无法联接表。DynoTable 的 SQL Workbench 可以通过 DynamoDB 真实的查询运行时物化数据,从而运行 INNER/LEFT JOIN(联接到分区键或 GSI 分区键)。
DynamoDB PartiQL 支持 GROUP BY 或 COUNT 吗?
不支持——DynamoDB PartiQL 中没有聚合,也没有 GROUP BY。COUNT/SUM/AVG/GROUP BY/HAVING 查询请使用 DynoTable 的 SQL Workbench。
为什么我的 SELECT * 成本这么高?
如果 WHERE 中没有分区键,PartiQL 会运行一次全表 Scan,并在过滤生效之前就对读取的每一个 item 计费。加上一个分区键谓词,就能把它变成一次 Query。
在 PartiQL 中该用单引号还是双引号?
字符串值用单引号('CUSTOMER#42'),表名和属性名这类标识符用双引号("AppData")。用双引号包裹一个值是最常见的 PartiQL 错误。
准备好对 DynamoDB 运行真正的 SQL 了吗?下载 DynoTable 并打开一个 Workbench 标签页。