数据类型概览

PostgreSQL 支持丰富的数据类型,是 SQL 标准实现最全面的数据库之一。

数值类型

-- 整数类型
SMALLINT      -- 2字节,范围 -32768 ~ 32767
INTEGER       -- 4字节,范围 -21亿 ~ 21亿
BIGINT        -- 8字节,范围超大

-- 精确小数
NUMERIC(10,2)  -- 总共10位,小数点后2位,精确计算
DECIMAL(10,2) -- NUMERIC 的别名

-- 浮点类型
REAL          -- 4字节,6位十进制精度
DOUBLE PRECISION -- 8字节,15位十进制精度

-- 自增类型
SMALLSERIAL   -- 2字节自增
SERIAL        -- 4字节自增(常用)
BIGSERIAL     -- 8字节自增

字符串类型

CHAR(n)       -- 定长字符串,不足补空格
VARCHAR(n)    -- 变长字符串,有长度限制(最常用)
TEXT          -- 不限长度字符串(无性能损失)

经验:不要被 CHAR 的"性能优势"迷惑。在 PostgreSQL 中,VARCHAR 和 TEXT 几乎没有性能差异。大多数场景直接使用 TEXT 最省心。如果需要限制长度,用 VARCHAR(n)

时间/日期类型

DATE          -- 日期(年月日)
TIME          -- 时间(时分秒,可带时区)
TIMESTAMP     -- 日期+时间(不带时区)
TIMESTAMPTZ   -- 日期+时间(带时区)
INTERVAL      -- 时间间隔(1 day '3 hours')

⚠️ 重要建议:数据库统一存储 UTC 时间,在前端展示时转换为用户本地时区。用 TIMESTAMPTZ 可以保留时区信息,但最佳实践是存入 UTC + 前端转换。

布尔类型

BOOLEAN        -- TRUE / FALSE / NULL
-- 输入时也支持:'yes'/'no', '1'/'0', 't'/'f'

JSON 类型

JSON          -- 文本格式的 JSON(保留格式,含空格/缩进)
JSONB         -- 二进制格式(去除空格,可建索引,处理更快)

绝大多数场景应该使用 JSONB

建表语法

完整建表示例

-- 创建 schema(命名空间)
CREATE SCHEMA IF NOT EXISTS school;

-- 创建一张完整的用户表
CREATE TABLE school.students (
    id SERIAL PRIMARY KEY,
    student_no VARCHAR(20) UNIQUE NOT NULL,  -- 学号,唯一约束
    name VARCHAR(50) NOT NULL,
    gender CHAR(1) CHECK (gender IN ('M', 'F', 'O')),  -- 检查约束
    birth_date DATE,
    email VARCHAR(200) UNIQUE,
    phone VARCHAR(20),
    address TEXT,
    enrollment_date DATE DEFAULT CURRENT_DATE,
    status VARCHAR(10) DEFAULT 'active' CHECK (status IN ('active', 'graduated', 'suspended')),
    gpa NUMERIC(3,2) DEFAULT 0.00 CHECK (gpa >= 0.00 AND gpa <= 4.00),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 查看表结构
\d school.students

约束类型

PostgreSQL 支持以下约束:

-- 1. NOT NULL — 不能为空
name VARCHAR(100) NOT NULL

-- 2. UNIQUE — 唯一
email VARCHAR(200) UNIQUE

-- 3. PRIMARY KEY — 主键(NOT NULL + UNIQUE)
id SERIAL PRIMARY KEY

-- 4. CHECK — 条件检查
price NUMERIC(10,2) CHECK (price > 0)
age INTEGER CHECK (age >= 0 AND age <= 150)

-- 5. DEFAULT — 默认值
created_at TIMESTAMPTZ DEFAULT NOW()

约束命名(推荐)

CREATE TABLE products (
    product_id SERIAL,
    product_name VARCHAR(100) NOT NULL,
    price NUMERIC(10,2),
    category VARCHAR(50),
    stock INTEGER DEFAULT 0,

    CONSTRAINT pk_products PRIMARY KEY (product_id),
    CONSTRAINT uq_products_name UNIQUE (product_name),
    CONSTRAINT ck_products_price CHECK (price > 0),
    CONSTRAINT ck_products_stock CHECK (stock >= 0)
);

有命名约束的好处:

  • 报错信息清晰:violates check constraint "ck_products_price"
  • 后续修改方便:ALTER TABLE ... DROP CONSTRAINT ck_products_price;

修改表

-- 添加列
ALTER TABLE students ADD COLUMN wechat VARCHAR(50);

-- 删除列
ALTER TABLE students DROP COLUMN wechat;

-- 修改列类型
ALTER TABLE students ALTER COLUMN grade TYPE VARCHAR(10);

-- 设置默认值
ALTER TABLE students ALTER COLUMN grade SET DEFAULT 'freshman';

-- 添加约束
ALTER TABLE students ADD CONSTRAINT ck_students_age CHECK (age >= 0);

-- 删除约束
ALTER TABLE students DROP CONSTRAINT ck_students_age;

-- 重命名列
ALTER TABLE students RENAME COLUMN grade TO class_level;

-- 重命名表
ALTER TABLE students RENAME TO scholar;

Schema 管理

-- 创建 schema(类似命名空间/目录)
CREATE SCHEMA sales;
CREATE SCHEMA hr;

-- 在 schema 中创建表
CREATE TABLE sales.orders (id SERIAL PRIMARY KEY, ...);
CREATE TABLE hr.employees (id SERIAL PRIMARY KEY, ...);

-- 查看所有 schema
\dn

-- 设置搜索路径(简化访问)
SET search_path TO sales, hr, public;

实战:创建一个电商数据库的初始表

-- 创建使用数据库
CREATE DATABASE ecommerce;
\c ecommerce

-- 用户表
CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(200) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    full_name VARCHAR(100),
    phone VARCHAR(20),
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 商品分类表
CREATE TABLE categories (
    category_id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    parent_id INTEGER REFERENCES categories(category_id),
    description TEXT
);

-- 商品表
CREATE TABLE products (
    product_id SERIAL PRIMARY KEY,
    category_id INTEGER REFERENCES categories(category_id),
    name VARCHAR(200) NOT NULL,
    description TEXT,
    price NUMERIC(10,2) NOT NULL CHECK (price > 0),
    stock INTEGER DEFAULT 0 CHECK (stock >= 0),
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 订单表(先建,供订单项引用)
CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(user_id),
    status VARCHAR(20) DEFAULT 'pending' 
        CHECK (status IN ('pending', 'paid', 'shipped', 'delivered', 'cancelled')),
    total_amount NUMERIC(12,2) NOT NULL DEFAULT 0,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- 订单项表(多对多关联表)
CREATE TABLE order_items (
    order_id INTEGER REFERENCES orders(order_id),
    product_id INTEGER REFERENCES products(product_id),
    quantity INTEGER NOT NULL CHECK (quantity > 0),
    unit_price NUMERIC(10,2) NOT NULL,
    PRIMARY KEY (order_id, product_id)
);

数据类型选择原则

  1. 能用 INT 不用 BIGINT:4 字节 vs 8 字节,百万行就省 4MB
  2. 金额用 NUMERIC,不用 FLOAT:浮点数的精度问题会导致金额计算出错
  3. JSONB > JSON:JSONB 支持索引和高效操作
  4. TIMESTAMPTZ > TIMESTAMP:时区信息避免混淆
  5. TEXT 足够就用 TEXT:不需要长度限制时,TEXT 就是最好的选择
  6. 用 SERIAL/BIGSERIAL 而非手动管理自增

小结

本文详细介绍了 PostgreSQL 丰富的数据类型体系,以及建表、修改表、约束管理等核心 DDL 操作。良好的表设计是高效查询的基础,选择合适的数据类型不仅是规范问题,更是性能问题。

下一步,我们将学习如何用 SELECT 查询和过滤数据。

Summary: 数据类型、建表语法、约束与电商表设计实战。