PostgreSQL: PostgreSQL数据表创建与修改

最后更新:2026-08-26

数据表是数据库中最核心的对象——它是存储数据的"电子表格",每一行是一条记录,每一列是一个字段。

1. 你将学到


2. 一个电商团队的真实故事

(1) 痛点:三张核心表怎么设计

Alice 的团队接到新需求,要给电商系统创建 3 张核心表:users(用户)、products(商品)、orders(订单)。需求如下:

Alice 不确定:约束该加在哪里?数据类型怎么选?外键级联删除怎么设置?

(2) CREATE TABLE + 约束的解法

PostgreSQL 支持在建表时直接声明所有约束,让数据库帮你保证数据质量:

SQL
-- Create users table with email uniqueness and auto-timestamp
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    name VARCHAR(100) NOT NULL,
    password_hash CHAR(60) NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Create products table with price > 0 check
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    price DECIMAL(10, 2) CHECK (price > 0),
    stock INTEGER DEFAULT 0 CHECK (stock >= 0),
    attributes JSONB DEFAULT '{}'::jsonb
);

-- Create orders table with foreign keys and status check
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
    product_id INTEGER REFERENCES products(id),
    quantity INTEGER NOT NULL CHECK (quantity > 0),
    status VARCHAR(20) DEFAULT 'pending'
        CHECK (status IN ('pending', 'paid', 'shipped', 'delivered', 'cancelled')),
    created_at TIMESTAMPTZ DEFAULT NOW()
);

(3) 收益


3. CREATE TABLE 语法

(1) 完整语法结构

SQL
CREATE TABLE [IF NOT EXISTS] table_name (
    column_name data_type [column_constraint ...],
    ...
    [, table_constraint ...]
);

(2) 列级约束

约束 语法 说明
PRIMARY KEY column_name type PRIMARY KEY 主键(唯一 + 非空)
NOT NULL column_name type NOT NULL 不允许 NULL
UNIQUE column_name type UNIQUE 值唯一
DEFAULT column_name type DEFAULT value 默认值
CHECK column_name type CHECK (condition) 条件检查
REFERENCES column_name type REFERENCES table(col) 外键引用

(3) 表级约束

SQL
-- Composite primary key
CREATE TABLE order_items (
    order_id INTEGER,
    product_id INTEGER,
    quantity INTEGER,
    PRIMARY KEY (order_id, product_id)
);

-- Named foreign key with cascade
CREATE TABLE comments (
    id SERIAL PRIMARY KEY,
    user_id INTEGER,
    content TEXT,
    CONSTRAINT fk_user
        FOREIGN KEY (user_id)
        REFERENCES users(id)
        ON DELETE SET NULL
);

4. 数据类型速览

PostgreSQL 支持超过 40 种数据类型,以下是最常用的:

类别 类型 说明 示例
整数 SMALLINT 2 字节,-32768 ~ 32767 年龄、数量小值
INTEGER (INT) 4 字节,-21 亿 ~ 21 亿 常用整数(推荐默认)
BIGINT 8 字节,±9.2 万亿 大 ID、金额(分)
自增 SERIAL 自增 INTEGER 主键 ID(推荐)
BIGSERIAL 自增 BIGINT 大表主键
IDENTITY SQL 标准自增(PG 10+) 新项目推荐
浮点 REAL 4 字节,6 位精度 科学计算
DOUBLE PRECISION 8 字节,15 位精度 科学计算
定点 DECIMAL(p,s) 精确小数 价格、金额(推荐)
NUMERIC(p,s) 同 DECIMAL 同上
字符串 VARCHAR(n) 变长,有上限 名称、邮箱
TEXT 变长,无上限 描述、内容
CHAR(n) 定长 哈希值、编码
布尔 BOOLEAN true / false / null 标志位
日期 DATE 日期 生日
TIMESTAMP 日期+时间(无时区) 本地时间
TIMESTAMPTZ 日期+时间+时区 全球应用(推荐)
INTERVAL 时间间隔 时长计算
UUID UUID 128-bit UUID 分布式 ID
JSON JSON 文本 JSON 兼容性好
JSONB 二进制 JSON(推荐) 索引查询快
💡 提示: PostgreSQL 中 VARCHAR 和 TEXT 的性能没有差异(与 MySQL 不同)。大多数场景推荐 VARCHAR(有长度限制时) 或 TEXT(无限制时)。


5. 约束详解

(1) 主键 PRIMARY KEY

100%
graph TB
    PK[PRIMARY KEY] --> U[UNIQUE<br/>No duplicate values]
    PK --> NN[NOT NULL<br/>Cannot be empty]
    PK --> IDX[Auto Index<br/>B-Tree index created automatically]

▶ 示例:单列和复合主键

SQL
-- Single-column primary key (most common)
CREATE TABLE categories (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

-- Composite primary key (for junction tables)
CREATE TABLE product_tags (
    product_id INTEGER REFERENCES products(id),
    tag_id INTEGER REFERENCES tags(id),
    PRIMARY KEY (product_id, tag_id)
);

输出:

TEXT 📖 仅展示
CREATE TABLE

(2) 外键 FOREIGN KEY

级联操作 ON DELETE 行为 ON UPDATE 行为
CASCADE 级联删除(删用户→删其订单) 级联更新
SET NULL 设为 NULL 设为 NULL
SET DEFAULT 设为默认值 设为默认值
RESTRICT 拒绝删除(默认行为) 拒绝更新
NO ACTION 同 RESTRICT(SQL 标准) 同 RESTRICT

▶ 示例:外键与级联操作

SQL
-- Order items: delete order -> delete all its items (CASCADE)
-- Product: delete product -> set product_id to NULL (SET NULL)
CREATE TABLE order_items (
    id SERIAL PRIMARY KEY,
    order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
    product_id INTEGER REFERENCES products(id) ON DELETE SET NULL,
    quantity INTEGER NOT NULL DEFAULT 1
);

输出:

TEXT 📖 仅展示
CREATE TABLE

(3) CHECK 约束

▶ 示例:CHECK 约束保护数据质量

SQL
-- Price must be positive, discount cannot exceed price
CREATE TABLE promotions (
    id SERIAL PRIMARY KEY,
    product_id INTEGER REFERENCES products(id),
    discount_price DECIMAL(10, 2) CHECK (discount_price > 0),
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    -- PG CHECK can reference other columns in the same row!
    CHECK (end_date > start_date)
    -- 注意:CHECK 不能跨表引用子查询,跨表校验需用触发器
);

输出:

TEXT 📖 仅展示
CREATE TABLE

6. ALTER TABLE

(1) 常用修改操作

操作 语法 说明
添加列 ALTER TABLE t ADD COLUMN col type 新增一列
删除列 ALTER TABLE t DROP COLUMN col 删除一列
重命名列 ALTER TABLE t RENAME COLUMN old TO new 列改名
修改类型 ALTER TABLE t ALTER COLUMN col TYPE new_type 修改数据类型
设默认值 ALTER TABLE t ALTER COLUMN col SET DEFAULT val 设置默认值
删默认值 ALTER TABLE t ALTER COLUMN col DROP DEFAULT 删除默认值
设 NOT NULL ALTER TABLE t ALTER COLUMN col SET NOT NULL 设非空
删 NOT NULL ALTER TABLE t ALTER COLUMN col DROP NOT NULL 取消非空
添加约束 ALTER TABLE t ADD CONSTRAINT name CHECK (...) 添加 CHECK
重命名表 ALTER TABLE old_name RENAME TO new_name 表改名

▶ 示例:修改表结构

SQL
-- Add a new column
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Add with default value
ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT true;

-- Change column type (requires USING for incompatible types)
ALTER TABLE users ALTER COLUMN phone TYPE TEXT;

-- Rename column
ALTER TABLE users RENAME COLUMN phone TO phone_number;

-- Set default value
ALTER TABLE users ALTER COLUMN is_active SET DEFAULT true;

-- Add CHECK constraint
ALTER TABLE products ADD CONSTRAINT price_reasonable
    CHECK (price < 100000);

-- Drop a column
ALTER TABLE users DROP COLUMN IF EXISTS phone_number;

-- Rename table
ALTER TABLE users RENAME TO customers;

输出:

TEXT 📖 仅展示
-- SQL 语句执行成功
⚠️ 注意: 修改列类型可能需要重写整个表(如 VARCHAR(50)→VARCHAR(100) 不需要,但 INTEGER→TEXT 需要)。大表操作时用 ALTER TABLE ... ALTER COLUMN ... TYPE ... USING ... 并确保 USING 子句正确转换。


7. DROP TABLE

▶ 示例:删除表

SQL
-- Safe delete (no error if table doesn't exist)
DROP TABLE IF EXISTS test_table;

-- Cascade delete (also drops dependent objects like views)
DROP TABLE IF EXISTS users CASCADE;

输出:

TEXT 📖 仅展示
-- SQL 语句执行成功
选项 说明
IF EXISTS 表不存在时不报错
CASCADE 同时删除依赖该表的对象(视图、外键引用等)
RESTRICT 有依赖对象时拒绝删除(默认行为)
🔥 易错: DROP TABLE 是不可逆操作!所有数据永久丢失。生产环境务必先 pg_dump 备份。


8. psql 查看表结构

命令 功能 示例
\dt 列出所有表 \dt
\dt+ 列出表(含大小信息) \dt+
\d tablename 查看表结构 \d users
\d+ tablename 查看表结构(详细) \d+ users

▶ 示例:查看表结构

BASH
# List all tables in current database
\dt

# Show users table structure
\d users

输出:

TEXT 📖 仅展示
                                        Table "public.users"
   Column    |          Type          | Collation | Nullable |              Default
-------------+------------------------+-----------+----------+-----------------------------------
 id          | integer                |           | not null | nextval('users_id_seq'::regclass)
 email       | character varying(255) |           | not null |
 name        | character varying(100) |           | not null |
 password_hash | character(60)        |           | not null |
 created_at  | timestamp with time zone |         |          | now()
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
    "users_email_key" UNIQUE CONSTRAINT, btree (email)

9. 完整示例:电商核心三表

SQL
-- ============================================
-- Complete example: e-commerce core tables
-- Users, Products, Orders with proper constraints
-- ============================================

-- 1. Users table
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    name VARCHAR(100) NOT NULL,
    password_hash CHAR(60) NOT NULL,
    role VARCHAR(20) DEFAULT 'customer'
        CHECK (role IN ('customer', 'admin', 'manager')),
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 2. Products table
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    description TEXT,
    price DECIMAL(10, 2) NOT NULL CHECK (price > 0),
    stock INTEGER DEFAULT 0 CHECK (stock >= 0),
    category VARCHAR(50),
    attributes JSONB DEFAULT '{}'::jsonb,
    is_available BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 3. Orders table with foreign keys
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    status VARCHAR(20) DEFAULT 'pending'
        CHECK (status IN ('pending', 'paid', 'shipped', 'delivered', 'cancelled')),
    total_amount DECIMAL(12, 2) DEFAULT 0 CHECK (total_amount >= 0),
    shipping_address TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 4. Order items table (junction table)
CREATE TABLE order_items (
    id SERIAL PRIMARY KEY,
    order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
    product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE RESTRICT,
    quantity INTEGER NOT NULL CHECK (quantity > 0),
    unit_price DECIMAL(10, 2) NOT NULL CHECK (unit_price > 0),
    subtotal DECIMAL(12, 2) GENERATED ALWAYS AS (quantity * unit_price) STORED
);

-- 5. Insert sample data
INSERT INTO users (email, name, password_hash) VALUES
    ('alice@example.com', 'Alice', '$2a$12$dummyhashforalice1234567890abcdefghijklmnopqr'),
    ('bob@example.com', 'Bob', '$2a$12$dummyhashforbob1234567890abcdefghijklmnopqrstuv');

INSERT INTO products (name, price, stock, category, attributes) VALUES
    ('Running Shoes', 89.99, 150, 'Footwear', '{"color": "red", "size": 42}'::jsonb),
    ('Laptop Backpack', 49.99, 300, 'Accessories', '{"color": "black", "material": "nylon"}'::jsonb);

-- 6. Verify table structure
\d users
\d products
\d orders
\d order_items

❓ 常见问题

Q SERIAL 和 IDENTITY 有什么区别?该用哪个?
A SERIAL 是 PG 专有语法,内部创建一个序列+设默认值。IDENTITY 是 SQL 标准语法(PG 10+),行为更严格(不能手动插入值除非 OVERRIDING SYSTEM VALUE)。新项目推荐用 IDENTITY。
Q VARCHAR 和 TEXT 该选哪个?
A PG 中 VARCHAR 和 TEXT 性能完全相同。VARCHAR(n) 有长度限制,TEXT 无限制。建议:有明确长度限制用 VARCHAR(如邮箱 VARCHAR(255)),无限制用 TEXT(如文章内容)。不要用 VARCHAR 不加长度——那等于 TEXT。
Q TIMESTAMP 和 TIMESTAMPTZ 该选哪个?
A 推荐始终用 TIMESTAMPTZ(带时区)。它存储 UTC 时间,显示时自动转换为客户端时区。TIMESTAMP 不带时区,跨时区应用会产生混乱。
Q 外键级联删除 CASCADE 安全吗?
A CASCADE 在开发环境很方便(删用户→删订单→删订单项,三级级联),但在生产环境要谨慎——误删一个用户可能连带删除大量数据。建议:关键业务表用 RESTRICT,日志/临时表用 CASCADE。
Q ALTER TABLE 修改大表会锁表吗?
A 简单操作(ADD COLUMN 设置 DEFAULT、改 VARCHAR 长度增大)不锁表。但改数据类型、加 NOT NULL 约束等需要全表扫描,会锁表。大表操作建议用 CREATE INDEX CONCURRENTLY(后续课程讲解)或维护窗口执行。
Q 一个表最多能有多少列?
A PG 单表最多 1600 列(实际受行大小 8KB 限制影响可能更少)。但超过 50 列通常是设计问题——考虑拆表或使用 JSONB 存储稀疏字段。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建一个 categories 表(id、name、description、created_at),其中 name 不能为空且唯一。插入 3 条测试数据后,用 \d categories 查看表结构。

  2. 进阶题(难度⭐⭐):在本课创建的电商三表基础上,用 ALTER TABLE 给 users 表添加 last_login_at TIMESTAMPTZ 列和 login_count INTEGER DEFAULT 0 列。然后添加一个 CHECK 约束确保 login_count 不能为负数。

  3. 挑战题(难度⭐⭐⭐):创建一个 product_reviews 表,包含:id(主键)、product_id(外键引用 products)、user_id(外键引用 users)、rating(1-5 分,CHECK 约束)、title、content、created_at。要求:删除用户时保留评论但 user_id 设为 NULL,删除商品时级联删除其所有评论。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏