# SQL 日志与追踪

调试 ORM 行为的核心手段是**看生成的 SQL**。`newSqlClient` 的 `sqlLogger` 选项控制打印形态：

```ts
const client = newSqlClient(driver, {
  sqlLogger: {
    pretty: true,                    // 换行缩进美化
    parameter: "PLACEHOLDER",        // 参数呈现方式
  },
})
```

## parameter 三种呈现

| 值 | 效果 |
|---|---|
| `PLACEHOLDER`（默认） | 参数位显示 `?`，参数值另行可见 |
| `COMMENT` | SQL 后追加注释（`-- $1 = 79.0`） |
| `INLINE` | 参数值直接内联进 SQL 文本（调试友好，注意别在生产开） |

## 沙盒里怎么用

沙盒每次运行都自动捕获 SQL 并显示在**结果面板的 SQL 追踪**区（等价于恒开日志）。演示：跑一次嵌套取形查询，看主查询与 `loadAssociation` 两段的真实 SQL：

```ts workspace
// ① 模型（完整可运行版见工作区）
import { model, prop, dto } from '@ts-grm/core'
import { sqlClient } from './infra/sql-client'

const BookStore = model("BookStore", "id", class {
    id = prop.i64()
    name = prop.str(100)
})

const Book = model("Book", "id", class {
    id = prop.i64()
    name = prop.str(50)
    edition = prop.i32()
    price = prop.num(10, 2)
    store = prop.m2o(BookStore).nullable()
})

// ② 嵌套取形：SQL 追踪会显示 主查询 + loadAssociation 两段
const rows = await sqlClient.createQuery(Book, (q, book) => {
    q.orderBy(book.name)
    return q.select(book.fetch(dto.view(Book, c => [
        c.id,
        c.name,
        c.store.with(c => [c.id, c.name]),
    ])))
}).fetchList()

console.log(rows.map(r => `${r.name} @ ${r.store?.name}`).join('\n'))
```

阅读 SQL 追踪的要点：

1. **看第二段的 IN 列表**——批加载按主查询收集的 key 合并，恒两段；
2. **对照预期形状**——视图选了哪些列、嵌套展开成几段，都一目了然；
3. 怀疑方言/优化问题时，把这段 SQL 复制到你的数据库 EXPLAIN。

## 生产建议

- 生产一般 `pretty: false` + `PLACEHOLDER`，避免日志膨胀；
- 连接驱动的原生日志（`pg` 的 pool 事件等）与 `sqlLogger` 是两层，按需取舍。
