# 运行 SQL 并读取结果

使用 SQL API 提交一条查询，取得 `query_id`，读取查询状态中的 `statement_id`，再获取结果行。创建请求成功不表示查询结果已经可读。

## 前提条件

- 已按[SQL 概览](../sql.md)准备认证信息和工作区作用域。
- 已确认目标数据库名称以及当前 MatrixOne 角色具有相应权限。
- 应用可以安全处理 SQL 文本和返回行，不把敏感内容完整写入日志。

```bash
export PRODUCT_API_BASE_URL='<Product API Base URL ending in /newmoi>'
export PRODUCT_API_KEY='<your-personal-access-token>'
export WORKSPACE_ID='<workspace-id>'
export DATABASE_NAME='<database-name>'
```

## 查询生命周期

```text
POST /query/execute
→ 保存 query_id
→ POST /query/describe
→ 检查 status 并保存 statement_id
→ POST /query/result
→ 读取 columns、result 和 total
```

`query_id` 标识本次 Product SQL 请求；`statement_id` 标识可读取结果和历史详情的 MatrixOne Statement。两者不能互换。

## 提交查询

```bash
curl -X POST "$PRODUCT_API_BASE_URL/query/execute" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d "{
    \"db_name\": \"$DATABASE_NAME\",
    \"query\": \"SELECT 1 AS n\",
    \"offset\": 0,
    \"limit\": 20
  }"
```

| 字段 | 类型 | 必需 | 说明 |
| --- | --- | --- | --- |
| `query` | string | 是 | 要运行的 SQL。调用方负责正确引用对象名和处理输入。 |
| `db_name` | string | 否 | 当前查询使用的数据库。涉及未限定表名时应明确提供。 |
| `sql_type` | string | 否 | SQL 来源或类型标识。只在调用场景有明确契约值时发送。 |
| `offset` | integer | 否 | 首次结果窗口的起始位置。示例值不代表固定默认值。 |
| `limit` | integer | 否 | 首次结果窗口的行数限制。服务端会限制实际返回规模。 |

SQL 接口当前返回 `code: 200` 的响应包络，与部分 Product API 页面中的 `code: "OK"` 不同：

```json
{
  "code": 200,
  "data": {
    "id": "<QUERY_ID>",
    "query_id": "<QUERY_ID>"
  }
}
```

保存 `data.query_id`。

## 读取查询状态

```bash
export QUERY_ID='<query-id-from-execute>'

curl -X POST "$PRODUCT_API_BASE_URL/query/describe" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d "{\"query_id\": \"$QUERY_ID\"}"
```

```json
{
  "code": 200,
  "data": {
    "query_id": "<QUERY_ID>",
    "statement_id": "<STATEMENT_ID>",
    "db_name": "<DATABASE_NAME>",
    "status": "<STATUS>",
    "err_msg": "",
    "rows_affected": 0
  }
}
```

| 字段 | 返回条件 | 说明 |
| --- | --- | --- |
| `data.query_id` | 查询存在时 | 与提交请求返回的查询 ID 对应。 |
| `data.statement_id` | Statement 已建立时 | 读取结果、详情和 Profile 时使用。 |
| `data.status` | 查询存在时 | 查询当前状态。只根据当前返回值决定继续等待、读取结果或处理失败。 |
| `data.err_msg` | 查询存在时 | 运行错误摘要；空字符串不替代对最终状态的判断。 |
| `data.rows_affected` | 运行产生该信息时 | 写操作影响的行数。 |

客户端应设置轮询间隔、总等待时间和最大次数。状态进入失败或完成后停止轮询；不要对同一个查询无限请求。

## 读取结果

取得 `statement_id` 后，请求结果窗口：

```bash
export STATEMENT_ID='<statement-id-from-describe>'

curl -X POST "$PRODUCT_API_BASE_URL/query/result" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d "{
    \"statement_id\": \"$STATEMENT_ID\",
    \"offset\": 0,
    \"limit\": 20
  }"
```

```json
{
  "code": 200,
  "data": {
    "query_id": "<QUERY_ID>",
    "statement_id": "<STATEMENT_ID>",
    "status": "<STATUS>",
    "offset": 0,
    "limit": 20,
    "total": 1,
    "columns": [
      {
        "name": "n",
        "type": "BIGINT"
      }
    ],
    "result": [
      [
        {
          "String": "1",
          "Valid": true
        }
      ]
    ]
  }
}
```

结果行按 `columns` 的顺序解释。每个单元格包含字符串表示和有效性标记；调用方应结合列类型完成转换，并正确处理空值。结果为空时，响应可能使用 `empty_set` 说明，不要把空结果当作请求失败。

## 取消查询

只有仍在运行的查询才有取消意义：

```bash
curl -X POST "$PRODUCT_API_BASE_URL/query/kill" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d "{\"query_id\": \"$QUERY_ID\"}"
```

取消请求返回后，再调用 `/query/describe` 确认状态。取消不会回滚查询在取消前已经提交的外部副作用或数据库事务结果。

## 使用 WebSocket

`GET /query/ws` 提供 SQL 编辑器 WebSocket 入口。使用 PAT 时，握手同时包含：

- `X-API-Key: <PAT>`；
- `X-Workspace-ID: <WORKSPACE_ID>`；
- `Sec-WebSocket-Protocol: moi-api-key, moi-api-key.<PAT>`。

不要把 PAT 放入 URL 查询参数。浏览器或 WebSocket 库应按其 API 分别设置普通 Header 和子协议。

## 常见问题

| 现象 | 先检查 | 下一步 |
| --- | --- | --- |
| 运行返回权限错误 | 工作区 ID、有效角色、数据库和对象权限 | 切换到有权限的角色后重新运行，不只检查工作区管理员身份。 |
| Result 提示找不到结果 | 是否使用 `statement_id`、查询是否已到可读状态 | 重新调用 Describe，取得最新 `statement_id` 和状态。 |
| 结果被截断 | `total`、`offset` 和 `limit` | 分页读取或使用结果下载接口，不一次加载全部数据。 |
| 查询长时间未完成 | `query_id`、状态和 `err_msg` | 停止无界轮询，按需要取消并保存标识用于排查。 |
| WebSocket 握手失败 | Header、子协议和 `ws`/`wss` 地址 | 按 HTTP Endpoint 的 Scheme 生成对应 WebSocket 地址，并重新设置子协议。 |

## 下一步

- [查看 SQL 历史与 Profile](metadata-history.md)
- [将 SQL 保存为工作簿版本](workbooks-versions.md)
- [使用 Product SDK 运行 SQL](../../../sdk/product-sdk/guides/sql.md)
