# SQL 编辑器

> DuckLocal 的 SQL 编辑器：查询标签页、运行、停止与格式化 SQL、就地标出错误、执行计划与按算子统计耗时的性能分析、基于表结构的自动补全和快捷键。

Source: https://ducklocal.app/docs/zh/sql-editor

**[English](../sql-editor.md)**

每个查询标签页都有独立的编辑器缓冲区。编辑器使用 tree-sitter 实现 SQL 语法高亮，采用 2 个空格缩进，并显示行号。

## 标签页

| 控件 | 作用 |
| --- | --- |
| `+` 或 **⌘T** | 在末尾新建一个查询标签页并切换过去 |
| 单击某个标签页 | 切换到该标签页 |
| **Ctrl+Tab** / **Ctrl+Shift+Tab** | 下一个 / 上一个标签页 |
| **⌘1** … **⌘8**、**⌘9** | 第一到第八个标签页；⌘9 是最后一个 |
| 拖动标签页 | 调整它在标签栏里的位置 |
| `×`、**⌘W** 或鼠标中键单击 | 关闭一个标签页。最后一个标签页关不掉：不显示 `×`，⌘W 也不起作用。有未保存修改的仪表盘（dashboard）会先询问 |
| 右键单击标签页 | **关闭标签页**、**关闭其他标签页**、**关闭右侧标签页**。有未保存修改的仪表盘会保持打开，并提示有几个 |
| 双击某个标签页 | 就地编辑标题。按 Enter 或点别处保留新标题，按 Escape 保留原标题；标题为空时回退为 `查询 N` |

打开的标签页下次启动会按原来的顺序回来：查询标签页连同标题和编辑器里的 SQL（不论运行过没有），以及仪表盘标签页。查询结果不保留，重新运行即可。新开的查询标签页以 `SELECT '你好，DuckLocal' AS greeting;` 开头。

## 工具栏 {#the-toolbar}

编辑器上方从左到右依次是：

| 按钮 | 作用 |
| --- | --- |
| **运行** | 运行编辑器里的 SQL，见[运行 SQL](#running-sql) |
| **停止** | 只在查询或性能分析运行期间出现，用来中断它 |
| **格式化** | 重新排版 SQL，见[格式化](#format) |
| **执行计划** | 显示 DuckDB 打算怎么执行，不真正运行语句 |
| **性能分析** | 运行查询，并显示每个算子花了多少时间 |

**执行计划** 和 **性能分析** 的说明见[执行计划与性能分析](#explain-and-profile)。

## 运行 SQL {#running-sql}

编辑器获得焦点时按 **⌘↵**（Cmd+Enter），或使用 **运行** 按钮。

查询运行期间，再按运行没有反应（执行计划和性能分析也一样），运行按钮旁会出现 **停止** 按钮。点停止或按 **⌘.** 可以中断查询：查询以 DuckDB 的 `Interrupted` 错误结束，和其他失败一样记入历史。没有超时：不点停止，查询会一直跑到 DuckDB 算完为止，其他数据库操作都在后面排队。

查询结果从 DuckDB 流式读取。表格达到[行数上限](results-and-charts.md#row-limits)后，扫描会停止，文件剩余部分不再读取。有些查询必须读取所有行才能返回第一行结果，例如 `ORDER BY`、`GROUP BY`、聚合和窗口函数，这类查询仍会完整计算。

每个标签页有自己的结果。切换标签页后才返回的结果，会回到发起查询的那个标签页。

语句怎么处理，看它开头的关键字。`SELECT`、`WITH`、`SHOW`、`DESCRIBE`、`EXPLAIN`、`PRAGMA`、`SUMMARIZE`、`VALUES`、`FROM`、`TABLE`、`PIVOT` 和 `CALL` 会产生结果集。其他语句（DDL（数据定义）和 DML（数据操作）语句）不显示结果表格，只报告 `完成：N 行受影响，耗时 …`。判断时会跳过开头的 `--` 注释，但不跳过块注释。

### 脚本

编辑器里可以写多条语句。**运行** 会像 DuckDB 命令行读文件那样按顺序全部执行，**遇到第一个失败就停下**：`ATTACH` 失败后再跑 `SELECT`，只会多出一个误导人的错误。结果上方有一条步骤栏，列出每条语句，标着行数、完成、失败或未执行；点哪一条就看哪一条的结果。出错时下划线标在失败的那条语句上，而不是整个脚本。

DuckDB 命令行的几个点命令也能用，每个单独占一行：

| 命令 | 显示 |
| --- | --- |
| `.tables ?PATTERN?` | 表和视图，可以用 `ILIKE` 模式过滤，比如 `stat%` |
| `.schema ?PATTERN?` | 表和视图的 `CREATE` 语句，可以过滤 |
| `.databases` | 已挂载的数据库 |
| `.help` | 这张列表 |

其他点命令（`.mode`、`.read` 等）会报错，并列出能用的那几个。

## 错误

语句执行失败时会弹出提示框，标题为 *查询失败*，里面原样显示 DuckDB 的报错信息。这条信息来自引擎，不管界面是什么语言，都是英文。结果表格里什么也不写，但这次执行仍会记入[查询历史](schema-and-history.md)，并标为失败。

报错里指明了位置时（解析错误和绑定错误都会带一行 `LINE n:` 和一个 `^`），编辑器会在该处画一条红色波浪线：单个 `^` 标出所指的词，一串 `^` 标出覆盖的范围。编辑代码时标记会跟着移动，下次运行时清除。运行期错误不带位置，所以不画标记。

## 格式化 {#format}

**格式化** 会用 `sqlformat` 处理缓冲区内容：缩进 2 个空格、关键字转为大写，然后替换整个缓冲区。缓冲区为空时什么也不做。

## 执行计划与性能分析 {#explain-and-profile}

这两个按钮都作用于编辑器里的全部文本，而且都只接受**恰好一条语句**。有两条或更多语句，或者有点命令（dot command）时，会拒绝执行并提示 *一次只能对一条语句执行计划或分析：把要看的那条单独放进一个标签页。* 末尾的 `;` 和末尾的注释没有影响。

运行、执行计划、性能分析三者中有一个在忙时，另外两个按了没有反应。执行计划和性能分析都不会记入[查询历史](schema-and-history.md#query-history)。它们的输出会替换 **结果** 标签页里的表格，当时开着别的标签页也会切回来；出错时和运行失败一样，显示 *查询失败* 提示框。

### 执行计划 {#explain}

**执行计划** 执行 `EXPLAIN <你的语句>`，用等宽纯文本显示 DuckDB 的查询计划和耗时。它只做规划，不执行语句，所以对 `INSERT`、`UPDATE`、`DELETE` 查看执行计划也是安全的。

### 性能分析 {#profile}

**性能分析** 用 `EXPLAIN (ANALYZE, FORMAT JSON)` 运行语句，把实际执行的计划画成一棵树：每个算子自身的耗时及其占比、产出的行数与优化器的预估（相差超过 10 倍时标出），以及它的过滤条件、连接键和投影列。最耗时的算子会在顶部点名，并用红色显示。怎么看这棵树，见[执行计划与性能分析的输出](results-and-charts.md#explain-and-profile)。

**性能分析会真正执行语句**，算子的耗时就是这样测出来的。所以它只接受单条只读查询：

- **接受：**`SELECT`、`WITH … SELECT`、`FROM …`、`VALUES`、`TABLE`、`SHOW`、`DESCRIBE`、`SUMMARIZE`。
- **拒绝：**任何写入数据的语句（`INSERT`、`UPDATE`、`DELETE`、`CREATE` 等）、`CALL`、`PRAGMA`、`COPY`、`EXPLAIN` 本身、藏在 `WITH` 后面的写入（`WITH s AS (…) INSERT …`），以及没有显式写出 `IN (…)` 列表的 `PIVOT`。

被拒绝的语句会提示 *只能分析读取数据的语句（SELECT、WITH、FROM 等）：分析会真正执行语句。* 判断由 DuckDB 自己的解析器在执行任何东西之前做出，而不是看开头的关键字。

性能分析的耗时和查询本身差不多，外加测量的开销。和运行一样，可以用 **停止**（或 **⌘.**）中断。

## 自动补全

补全菜单会参考 catalog 中的对象。候选项依次为：

1. schema（表结构）中的表和列（最多 35 项）
2. DuckDB 函数名
3. SQL 关键字

最多列出 50 个候选项。匹配对象是光标左边紧挨着的标识符，按**前缀**匹配，不区分大小写：`sel` 能补全为 `SELECT`，`elect` 则什么也补不出来。选中函数会插入函数名和左括号；包含空格或特殊字符的标识符会加上双引号再插入。

关键字和函数列表只收录了挑选过的一部分，并非所有 DuckDB 函数都会出现。

## 键盘快捷键

在 Windows 和 Linux 上，下文的 **⌘** 一律换成 **Ctrl**。

DuckLocal 自己的快捷键，全部列在菜单栏里：

| 快捷键 | 操作 |
| --- | --- |
| ⌘↵ | 运行查询 |
| ⌘. | 停止正在运行的查询 |
| ⌘T | 新建查询标签页 |
| ⌘W | 关闭当前标签页 |
| Ctrl+Tab / Ctrl+Shift+Tab | 下一个 / 上一个标签页（Mac 上也是 Ctrl） |
| ⌘1 … ⌘9 | 跳到对应位置的标签页；⌘9 是最后一个 |
| ⌘O | 打开数据 |
| ⌘S | 在仪表盘的源码视图中保存 |
| ⌘B | 收起或展开侧栏 |
| ⌘+ / ⌘− / ⌘0 | 放大 / 缩小 / 复原界面大小——见[界面大小](settings-and-data.md#interface-size) |
| ⌘Q | 退出 |

编辑器自带的快捷键，和普通文本框一样：

| 快捷键 | 操作 |
| --- | --- |
| ⌘F / ⇧⌘F | 在编辑器中查找 / 替换 |
| ⌘Z / ⇧⌘Z | 撤销 / 重做 |
| ⌘A | 全选 |
| ⌘C / ⌘X / ⌘V | 复制 / 剪切 / 粘贴 |
| ⌥⌘↑ / ⌥⌘↓ | 在上方 / 下方添加光标 |
| ⌘← / ⌘→ / ⌥← / ⌥→ | 按行 / 按词移动 |
| ⌘↑ / ⌘↓ | 移动到文档开头 / 结尾 |
| ⌘⌫ / ⌥⌫ | 删除到行首 / 删除前一个词 |

## 菜单

菜单栏列出了窗口里的所有命令和对应的快捷键：

| 菜单 | 命令 |
| --- | --- |
| DuckLocal | 命令行与 AI、检查更新…、启动时检查更新、显示新手引导、打开日志文件夹、退出 DuckLocal |
| 文件 | 新建查询、打开数据、保存、关闭标签页、下一个标签页、上一个标签页 |
| 编辑 | 撤销、重做、剪切、拷贝、粘贴、全选 |
| 查询 | 运行、停止 |
| 显示 | 显示或隐藏侧边栏、放大界面、缩小界面、默认大小、在线底图（OpenStreetMap） |

Windows 和 Linux 没有菜单栏，**显示新手引导** 和 **打开日志文件夹** 在状态栏右端版本号的右键菜单里。

**在线底图（OpenStreetMap）** 是一个勾选项，见[图表](results-and-charts.md#charts)。表结构侧栏里的行也有右键菜单，见[Schema 浏览与历史](schema-and-history.md#right-click-menu)。没有命令面板。
