[ PROMPT_NODE_24060 ]
D1 常见陷阱
[ SKILL_DOCUMENTATION ]
# D1 常见问题与排查
## 常见错误
### "SQL Injection Vulnerability" (SQL 注入漏洞)
**原因:** 使用字符串插值而不是带 bind() 的预处理语句。
**解决方案:** 始终使用预处理语句:`env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).all()`,不要使用允许攻击者注入恶意 SQL 的字符串插值。
### "no such table" (表不存在)
**原因:** 未运行迁移导致表不存在,或使用了错误的数据库绑定。
**解决方案:** 使用 `wrangler d1 migrations apply --remote` 运行迁移,并验证 wrangler.jsonc 中的绑定名称是否与代码匹配。
### "UNIQUE constraint failed" (唯一约束失败)
**原因:** 尝试在具有 UNIQUE 约束的列中插入重复值。
**解决方案:** 捕获错误并返回 409 Conflict 状态码。
### "Query Timeout (30s exceeded)" (查询超时)
**原因:** 查询执行超过 30 秒超时限制。
**解决方案:** 将查询拆分为更小的查询,添加索引以加速查询,或减小数据集大小。
### "N+1 Query Problem" (N+1 查询问题)
**原因:** 在循环中进行多次单独查询,而不是使用单个优化查询。
**解决方案:** 使用 JOIN 在单个查询中获取相关数据,或使用 `batch()` 方法进行多次查询。
### "Missing Indexes" (缺少索引)
**原因:** 查询在没有索引的情况下执行全表扫描。
**解决方案:** 使用 `EXPLAIN QUERY PLAN` 检查是否使用了索引,然后使用 `CREATE INDEX idx_users_email ON users(email)` 创建索引。
### "Boolean Type Issues" (布尔类型问题)
**原因:** SQLite 使用 INTEGER (0/1) 而不是原生布尔类型。
**解决方案:** 处理布尔值时绑定 1 或 0,而不是 true/false。
### "Date/Time Type Issues" (日期/时间类型问题)
**原因:** SQLite 没有原生的 DATE/TIME 类型。
**解决方案:** 使用 TEXT (ISO 8601 格式) 或 INTEGER (Unix 时间戳) 来存储日期/时间值。
## 计划层级限制
| 限制 | 免费层级 | 付费计划 | 备注 |
|-------|-----------|------------|-------|
| 数据库大小 | 500 MB | 10 GB | 付费版可为每个租户设计多个数据库 |
| 行大小 | 1 MB | 1 MB | 大文件存储在 R2 中,而非 D1 |
| 查询超时 | 30s | 30s (使用会话可达 900s) | 迁移使用会话 API |
| 批量大小 | 1,000 条语句 | 10,000 条语句 | 相应拆分大型批次 |
| 时间旅行 | 7 天 | 30 天 | 时间点恢复窗口 |
| 读取副本 | ❌ 不可用 | ✅ 可用 | 用于降低延迟的付费插件 |
| 会话 API | ❌ 不可用 | ✅ 最长 15 分钟 | 用于迁移和繁重操作 |
| 并发请求 | 10,000/分钟 | 高