# Schema 浏览与历史

> 在 DuckLocal 侧栏浏览数据库、表、视图与列，复制表信息用于提示词，一键生成 SELECT 查询、修改列类型。管理 DuckDB 扩展，并复用历史查询。

Source: https://ducklocal.app/docs/zh/schema-and-history

**[English](../schema-and-history.md)**

侧栏有三个标签页：**表结构**、**查询历史** 和 **扩展**（英文界面中为 **Schema**、**History** 和 **Extensions**）。点标题栏最左侧的按钮，或按 **⌘B**，可以收起或展开侧栏；无论侧栏是否收起，这个按钮都在原处。侧栏收起与否，DuckLocal 会记住。

## Schema 树

树的上方是几个分区：**本地文件**、**仪表盘** 和 **S3**，显示为灰色小标题；下面是各个数据库，显示为加粗的行：

- **本地文件**：已注册的数据文件，以视图名显示，附带行数。展开后是该视图的列。
  - 文件名与视图名不同（如 `sales.csv` 对应 `sales_2`）时，旁边才显示文件名。
  - 两个文件视图名相同时，显示所在文件夹。
  - 鼠标悬停可看到完整路径。
- **仪表盘**：最近打开过的 `.dash` 仪表盘，按名称排序，所以点击后条目不会挪位置。
  - 点一下就像打开文件一样，在标签页中重新打开。
  - 两个仪表盘同名时，旁边显示各自所在的文件夹。同一个文件换个写法（`E:\Proj` 和 `E:\proj`，或相对路径和绝对路径）只算一条，也只开一个标签页。
  - 悬停时出现的 × 只是把条目移出列表，文件和已打开的标签页都不受影响。
  - 路径已不存在的条目会自动移出列表。
- **S3**：[配置好 S3](s3.md) 之后出现，内容按需加载。
- 各个数据库，下面依次是 schema、表和视图、列。窗口打开的数据库排在最前；[附加进来的数据库](data-sources.md#attached-databases)跟在后面，以只读方式附加的会标上“只读”。

表会显示预估行数，视图则不显示。列信息会标出 DuckDB 数据类型。系统数据库以及 `information_schema`、`pg_catalog`、`system` 这几个 schema 不会出现在列表中。

树上方的搜索框（**搜索表或列**）只保留名字里含有输入内容的行（不区分大小写），以及它们的上级。名字匹配的表保留它的所有列；因为某一列匹配而保留的表会自动展开，显示那一列。清空搜索框即可恢复整棵树。

### 数据库 {#databases}

当前打开的数据库排在最前，其后每个[附加的数据库](data-sources.md#attached-databases)各自成组，名称旁显示表的数量。

- 附加的数据库即使是空的也会显示。当前打开的数据库在没有任何表时不显示，全新的内存会话就是这样开始的。
- 名称旁的浅色提示会标出 **只读**，文件类型不是 DuckDB 时还会标出类型（**sqlite**）。
- 鼠标悬停在名称上，可以看到数据库文件的路径。

右键数据库可以把它设为默认数据库，或者分离它，见[右键菜单](#right-click-menu)。

标签页旁的刷新按钮（**刷新 Schema**）会重新加载 catalog 和已注册文件的列表。

## 生成 SELECT

下面这些点击操作都会**替换当前标签页的全部内容**，不会插入到光标处。点击查询历史条目也是如此。操作前，请先保存需要保留的编辑器内容。

点击下面任意一项，都会把 SQL 填进当前编辑器：

| 点击 | 生成的 SQL |
| --- | --- |
| 表的播放按钮 | `SELECT * FROM db.schema.table LIMIT 100;` |
| 列的复选框，或列名 | `SELECT col FROM db.schema.table LIMIT 100;` |
| 同一张表的更多列 | `SELECT a, b FROM db.schema.table LIMIT 100;` |
| 是数据文件的 S3 对象 | `SELECT * FROM 's3://bucket/key' LIMIT 100;` |

每一列（表、视图或本地文件的列）前面都有一个复选框。勾选列，或者点击列名（等于勾选它），会用勾选的列生成 `SELECT`，列的顺序按表里的顺序，而不是勾选的先后。取消最后一个勾选后变成 `SELECT *`。勾选只属于一张表：勾选另一张表的列，就从那张表重新开始。

生成的 SQL 分三行（`SELECT …`、`FROM …`、`LIMIT 100;`）。表名按当前默认数据库和 schema 尽量写短：在默认 schema 里的表写成 `orders`，同一数据库其他 schema 里的写成 `main.orders`，其余写成 `database.schema.table`。对 `snap` 执行 **设为默认数据库** 之后，预览它的表就是 `FROM orders`。第一条查询运行之前，名称一律写完整。

名称只有包含字母、数字和下划线以外的字符时才加双引号。

## 右键菜单 {#right-click-menu}

行上悬停按钮提供的命令，右键菜单里都有，不必悬停也能找到：

| 行 | 菜单 |
| --- | --- |
| 表或视图 | 生成 SELECT 查询、列概览、复制名称、复制表信息 |
| 列 | 生成 SELECT 查询、复制名称、修改数据类型（仅表的列） |
| 本地文件 | 生成 SELECT 查询、列概览、复制名称、复制表信息、从本地文件移除 |
| 数据库 | 设为默认数据库、分离数据库（仅附加的数据库） |
| S3 分组 | 刷新 bucket 列表 |
| 最近的仪表盘 | 从列表移除 |

**复制名称** 只复制名字本身，即表名、列名或视图名，不带数据库和 schema 前缀，也不加引号。

**复制表信息** 把整张表复制成 Markdown，可以直接贴进聊天、issue 或 AI agent 的提示词：查询时用的全名、是表还是视图、大概多少行，以及每一列和它的类型：

```md
memory.main.orders (table, ~224 rows, 3 columns)

| column | type |
|---|---|
| date | DATE |
| channel | VARCHAR |
| revenue | DOUBLE |
```

**列概览** 会把当前编辑器的内容替换为 `FROM <表>;` 并运行，然后把结果面板切到 **概览** 标签页，汇总整张表每一列的情况：空值、不同值个数、取值范围，以及一张展示取值分布的小图。见[列概览](results-and-charts.md#overview)。

**设为默认数据库** 会对该数据库执行 `USE`，此后不带数据库前缀的表名都在它里面查找，生成的查询也会更短（见上文）。已经是默认数据库的那一个不显示这一项。

**分离数据库** 会分离一个附加的数据库并忘掉它，下次不会再自动附加。如果它正是默认数据库，会先把会话切到另一个数据库。文件本身不受影响。当前打开的数据库不能分离，所以它的菜单里没有这一项。详见[附加数据库](data-sources.md#attached-databases)。

## 修改列的数据类型

列行提供修改数据类型的操作（视图列上不可用，因此挂载的 CSV 和 Parquet 列无法改类型）。点开后是一个可自由输入的对话框，预先填好当前类型，确认后执行：

```sql
ALTER TABLE db.schema.table ALTER COLUMN col SET DATA TYPE <what you typed>
```

这里的表名总是写完整，列名只在需要时才加引号。

没有下拉选择，也没有可选类型列表，任何 DuckDB 类型字符串都行，包括 `DECIMAL(10,2)` 这样带参数的类型。类型无效时会弹出错误通知。修改成功后重新加载 catalog。

目前不支持重命名和删除列，也不支持编辑列注释。

## 移除已注册的文件

点文件行上的 **×**，确认后会删除该视图并清除注册记录。**磁盘上的文件不会被改动。**

要批量移除登记，先在 **本地文件** 下选中这些文件：

- macOS 用 **⌘-点击**，Windows/Linux 用 **Ctrl-点击**，添加或取消一个文件的选择。
- **Shift-点击** 选择连续的一段文件。
- 点击已选文件上的 **×**，或使用它的右键移除操作，再确认批量移除。菜单会显示将移除的文件数量。

这只移除视图和登记，磁盘上的原文件保留。这种多选不用于数据库表或仪表盘筛选器。

## 扩展 {#extensions}

**扩展** 标签页列出引擎知道的所有 DuckDB 扩展：已加载的在前，其次是已安装的，最后是其余的，每组按名称排序。

每一行显示：

- 扩展名和一个状态标签：**已加载**、**已安装** 或 **未安装**；
- DuckDB 对它的一行说明；
- 版本和来源：**内置**，或安装时所用的仓库（`core`、`community` 或一个 URL）。

状态的含义：

| 状态 | 含义 |
| --- | --- |
| 已加载 | 在当前连接中生效：它的函数、读取器和类型现在就能在查询里用 |
| 已安装 | 已下载到这台机器上，但当前连接还没有加载 |
| 未安装 | DuckDB 知道它，但这台机器上还没有 |

**内置** 扩展编译在 DuckLocal 里，其中包括 `parquet` 和 `json`，两者一开始就已加载。内置扩展不需要安装，也不会更新。

每一行只显示适用的操作：

| 按钮 | 何时显示 | 执行 |
| --- | --- | --- |
| **安装** | 未安装 | `INSTALL name` |
| **加载** | 已安装但未加载 | `LOAD name` |
| **更新** | 从仓库安装的 | `UPDATE EXTENSIONS (name)` |

- **安装** 和 **更新** 要从 DuckDB 的扩展仓库下载，所以需要联网。**加载** 读取已安装的文件，不联网。
- 安装不等于加载。装好后再点 **加载**，或运行 `LOAD name`。
- 装好的扩展会一直保留：它是磁盘上的文件，放在 DuckDB 自己的扩展目录里。加载只对当前连接有效。
- 这个目录按 DuckDB 版本区分（`~/.duckdb/extensions/v2.0.0/…`）。DuckLocal 的更新换了 DuckDB 版本时（比如 0.3.0 换成了 DuckDB 2.0），原来装过的扩展会显示为 **未安装**，需要重新安装。
- 同一时间只执行一个操作，其间其他按钮不可用。完成后会弹出通知：*已安装 …*、*已加载 …* 或 *已更新 …*，失败时说明原因。
- 加载或更新之后，Schema 树会重新加载，因为扩展可能带来新的表和函数。

列表从 `duckdb_extensions()` 读取，时机是：每次打开这个标签页时、每次操作之后、标签页开着时连接发生变化，以及点击标签页旁的刷新按钮（**刷新扩展列表**）时。所以用别的方式加载的扩展也会显示出来，比如在查询标签页里输入的 `LOAD`，或者 DuckDB 自己加载的扩展。

从 v0.2.1 起，窗口和 CLI 都不会在查询需要某个扩展时自动下载它。请在 **扩展** 标签页或通过 `INSTALL name` 显式安装。已经装在本机的扩展仍可自动加载。

### 打开 DuckDB UI {#duckdb-ui}

[DuckDB UI](https://duckdb.org/2025/03/12/duckdb-ui) 是 DuckDB 官方的浏览器 Notebook，由 MotherDuck 开发。它和其他扩展一样，也能在 DuckLocal 里运行：

1. 在 **扩展** 标签页的 `ui` 一行点击 **安装**，再点击 **加载**。
2. 在查询标签页里运行：

   ```sql
   CALL start_ui();
   ```

   浏览器会打开 `http://localhost:4213` 上的 UI。如果只想启动服务、不打开浏览器，改用 `CALL start_ui_server();`。

![扩展标签页中已加载的 ui 扩展](../assets/duckdb-ui.png)

UI 运行在启动它的那个 DuckDB 实例上，这里就是 DuckLocal 的实例。需要注意：

- **它需要联网。** 和其他扩展一样，安装 `ui` 要下载；每次启动时，扩展的本地服务还会从 `https://ui.duckdb.org` 拉取 UI 自身的前端文件。查询和数据仍然在本地执行。
- **它随 DuckLocal 一起结束。** 退出 DuckLocal 会关闭它的 DuckDB 实例，服务也随之停止；也可以用 `CALL stop_ui_server();` 提前停止。
- **Notebook 由扩展保存，而不是 DuckLocal，** 位置在 `~/.duckdb/extension_data/ui/`。在 UI 里执行的查询不会出现在 DuckLocal 的查询历史中。
- **它不能直接运行在只读数据库上。** 可以在 Notebook 里用 `ATTACH '…' (READ_ONLY)` 附加那个数据库。

[DuckDB UI 与 DuckLocal 对比](https://ducklocal.app/zh/blog/duckdb-ui-vs-ducklocal/)一文比较了两者，以及各自适合的场景。

## 查询历史 {#query-history}

每次运行都会记录下来，成功失败都记，内容包括本地时间戳、耗时（毫秒）、行数（DML 为受影响行数），失败时还有错误文本。失败的条目显示为红色，带 *失败* 标签。

列表显示最新的 **200** 条记录。点击某一行，该 SQL 会填回当前编辑器并获得焦点，替换掉原有内容。

历史记录不设保留期限，也不自动清理：历史表会一直增长，界面只显示最近 200 条。也没有搜索、过滤、导出功能，无法在界面上删除单条记录。

查询历史存在 DuckLocal 自己的数据库文件里，不在当前打开的数据库中，所以切换数据库、丢失工作区后都还在。文件位置见[设置与应用数据](settings-and-data.md)。
