# CREATE TABLE

> 创建表、检查其列，并了解临时表和不记录 WAL 的表。

---

LLMS 索引： [llms.txt](/llms.txt)

---

PostgreSQL 通过表存储数据。`CREATE TABLE` 通过列出列及其数据类型来定义新表。默认值、约束、索引和外键都可以在创建时添加，但最小的表只需要一个名称和列清单。本课保持简洁；约束会在专门的课程中介绍。

## 本章要点 {#learn}

- 使用 `CREATE TABLE` 定义表
- 为每列选择数据类型（`integer`、`text` 等）
- 使用 `DROP TABLE IF EXISTS` 使建表脚本幂等
- 检查 `public` 模式中的表
- `TEMP TABLE`、普通表和 `UNLOGGED TABLE` 的区别

## 示例 {#example}

```sql {num="16.1" caption="CREATE TABLE"}
-- Drop the table if it exists so the script is idempotent
DROP TABLE IF EXISTS fruits;

-- A minimal table: id and name
CREATE TABLE fruits (
  id   integer,
  name text
);

-- Confirm it exists in the current schema
SELECT schemaname, tablename
FROM pg_tables
WHERE tablename = 'fruits';

-- See the columns and their types
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'fruits'
ORDER BY ordinal_position;
```

`fruits` 有两列：`id`（32 位有符号整数）和 `name`（变长文本）。`CREATE TABLE` 中列的顺序，就是行的*物理*存储顺序——为了提高宽表的存储效率，应将定长列放在一起。对于小表，这并不重要。

`DROP TABLE IF EXISTS` 只在表存在时删除表；如果没有 `IF EXISTS`，该语句在全新的数据库中就会报错。将它与 `CREATE TABLE` 结合后，在迭代数据库模式的过程中可以安全地反复运行脚本。当然，在生产环境中不要盲目删除表——请使用迁移。

有几种 `CREATE TABLE` 的变体值得了解：

- `CREATE TEMP TABLE`（或 `CREATE TEMPORARY TABLE`）创建的表只在当前会话中存在，并会自动删除。
- `CREATE UNLOGGED TABLE` 不写入预写式日志——写入更快，但服务器崩溃时内容会被清空。适合存放可重建的派生数据或暂存数据。
- `CREATE TABLE ... (LIKE other_table INCLUDING ALL)` 会复制另一个表的模式。
- `CREATE TABLE ... AS SELECT ...` 会根据查询创建并填充一个表（称为 *CTAS*）。

## 运行示例 {#run}

运行方式：

```text
$ psql -f source/create-table.sql postgres
DROP TABLE
CREATE TABLE
 schemaname | tablename
------------+-----------
 public     | fruits
(1 row)

 column_name | data_type | is_nullable
-------------+-----------+-------------
 id          | integer   | YES
 name        | text      | YES
(2 rows)
```

## 常见陷阱 {#pitfalls}

- 不要因为在 MySQL 中学到 `varchar(255)` 就选择它：在 PostgreSQL 中，`text` 和 `varchar(n)` 的性能相同。除非你明确需要长度上限，否则使用 `text`。
- 忘记在本不应为空的列上添加 `NOT NULL`——以后可以再加，但从一开始就正确设置更容易。
- 将列命名为 `user`、`order`、`select`——这些都是保留字。可以改名（`user_id`、`placed_at`），也可以加引号（`"user"`），但这样会一直带来麻烦。

> [!TIP] 提示
> 值得记住的标准列类型：`integer`（32 位）、`bigint`（64 位）、`text`、`boolean`、`date`、`timestamp with time zone`（几乎总是优先于 `timestamp`）、用于精确小数的 `numeric(p,s)`，以及用于不透明 ID 的 `uuid`。每种类型都有专门的课程。

> [!NOTE] 动手练习
> 添加第三列，例如 `price numeric(10,2)`，然后再次运行脚本。接着将 `integer` 改为 `bigint`，再运行一次。在交互式 psql 会话中使用 `\d fruits` 检查。

## 下载 {#downloads}

SQL 文件：[create-table.sql](/examples/source/create-table.sql) · [全部示例文件](/examples/examples.zip) · [准备工作](/#prerequisites)
