Skip to content

默认值 (Default Values)

概述

在 PostgreSQL 中,默认值(Default Values) 是数据库表列的一个重要特性,它允许我们为列预设一个值。当插入新记录时,如果没有为某个列明确指定值,系统会自动使用该列的默认值。这个特性大大简化了数据插入操作,提高了开发效率。

默认值的类型

1. 常量默认值

最简单的默认值类型,直接指定一个固定值:

sql
CREATE TABLE users (
    id integer,
    username text,
    status text DEFAULT 'active',
    is_verified boolean DEFAULT false,
    created_date date DEFAULT CURRENT_DATE
);

示例演示:

sql
-- 插入用户,只指定必要字段
INSERT INTO users (id, username) VALUES (1, 'alice');

-- 查看结果
SELECT * FROM users;

输出:

idusernamestatusis_verifiedcreated_date
1aliceactivefalse2024-03-20

2. 表达式默认值

默认值可以是一个表达式,这个表达式会在每次插入时重新计算:

sql
CREATE TABLE orders (
    order_id integer,
    customer_name text,
    order_date timestamp DEFAULT CURRENT_TIMESTAMP,
    order_number text DEFAULT 'ORD-' || EXTRACT(EPOCH FROM CURRENT_TIMESTAMP)::text
);

重要特性:

  • 表达式在每次插入时计算,不是在创建表时
  • 可以使用函数、运算符和常量

示例演示:

sql
-- 第一次插入
INSERT INTO orders (order_id, customer_name) VALUES (1, '张三');

-- 等待几秒后第二次插入
INSERT INTO orders (order_id, customer_name) VALUES (2, '李四');

SELECT * FROM orders;

输出示例:

order_idcustomer_nameorder_dateorder_number
1张三2024-03-20 14:30:15.123ORD-1710934215
2李四2024-03-20 14:30:20.456ORD-1710934220

3. 函数默认值

常用的函数默认值包括:

sql
CREATE TABLE activity_log (
    id integer,
    action text,
    -- 时间相关函数
    created_at timestamp DEFAULT CURRENT_TIMESTAMP,
    created_date date DEFAULT CURRENT_DATE,
    created_time time DEFAULT CURRENT_TIME,
    
    -- UUID 生成(需要 uuid-ossp 扩展)
    uuid_id uuid DEFAULT gen_random_uuid(),
    
    -- 随机数
    random_score numeric DEFAULT random()
);

序列号默认值

传统方式:使用 nextval()

序列号是数据库中非常常见的需求,PostgreSQL 提供了序列对象来生成连续的数字:

sql
-- 创建序列
CREATE SEQUENCE products_product_no_seq;

-- 创建表,使用序列作为默认值
CREATE TABLE products_advanced (
    product_no integer DEFAULT nextval('products_product_no_seq'),
    name text NOT NULL,
    price numeric DEFAULT 9.99
);

示例演示:

sql
-- 插入数据,不指定 product_no
INSERT INTO products_advanced (name, price) VALUES ('商品A', 19.99);
INSERT INTO products_advanced (name, price) VALUES ('商品B', 29.99);
INSERT INTO products_advanced (name) VALUES ('商品C');

SELECT * FROM products_advanced;

输出:

product_nonameprice
1商品A19.99
2商品B29.99
3商品C9.99

现代方式:使用 SERIAL 类型

PostgreSQL 提供了 SERIAL 类型作为序列号的简写方式:

sql
CREATE TABLE products_serial (
    product_no SERIAL,              -- 等价于 integer DEFAULT nextval('...')
    name text NOT NULL,
    price numeric DEFAULT 9.99
);

SERIAL 类型对比:

类型范围存储大小等价写法
SERIAL1 到 2,147,483,6474 字节integer DEFAULT nextval('seq')
BIGSERIAL1 到 9,223,372,036,854,775,8078 字节bigint DEFAULT nextval('seq')
SMALLSERIAL1 到 32,7672 字节smallint DEFAULT nextval('seq')

SERIAL 的优势

使用 SERIAL 类型时,PostgreSQL 会自动:

  1. 创建对应的序列对象
  2. 设置默认值为 nextval()
  3. 设置序列的所有者为该列
  4. 删除表时自动删除序列

高级默认值示例

1. 条件默认值

虽然 PostgreSQL 不直接支持条件默认值,但可以通过函数实现:

sql
-- 创建函数生成条件默认值
CREATE OR REPLACE FUNCTION get_default_status() 
RETURNS text AS $$
BEGIN
    -- 根据当前时间决定状态
    IF EXTRACT(hour FROM CURRENT_TIME) BETWEEN 9 AND 17 THEN
        RETURN 'working_hours';
    ELSE
        RETURN 'after_hours';
    END IF;
END;
$$ LANGUAGE plpgsql;

-- 使用函数作为默认值
CREATE TABLE tasks (
    id SERIAL,
    title text,
    status text DEFAULT get_default_status(),
    created_at timestamp DEFAULT CURRENT_TIMESTAMP
);

2. 复杂表达式默认值

sql
CREATE TABLE financial_records (
    id SERIAL,
    amount numeric,
    currency text DEFAULT 'CNY',
    exchange_rate numeric DEFAULT 1.0,
    -- 计算默认的人民币金额
    cny_amount numeric DEFAULT NULL,
    -- 生成默认的记录编号
    record_code text DEFAULT 'FIN-' || to_char(CURRENT_DATE, 'YYYYMMDD') || '-' || 
                             lpad(EXTRACT(EPOCH FROM CURRENT_TIMESTAMP)::text, 10, '0')
);

实际应用场景

场景1:用户管理系统

sql
CREATE TABLE users_system (
    -- 主键,自动递增
    user_id SERIAL PRIMARY KEY,
    
    -- 用户名,必填
    username text NOT NULL UNIQUE,
    
    -- 邮箱,必填
    email text NOT NULL UNIQUE,
    
    -- 状态,默认为激活
    status text DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'suspended')),
    
    -- 角色,默认为普通用户
    role text DEFAULT 'user' CHECK (role IN ('admin', 'user', 'guest')),
    
    -- 是否验证邮箱,默认未验证
    email_verified boolean DEFAULT false,
    
    -- 创建时间,自动设置
    created_at timestamp DEFAULT CURRENT_TIMESTAMP,
    
    -- 更新时间,初始等于创建时间
    updated_at timestamp DEFAULT CURRENT_TIMESTAMP,
    
    -- 最后登录时间,初始为空
    last_login timestamp DEFAULT NULL
);

使用示例:

sql
-- 简单插入,只提供必要信息
INSERT INTO users_system (username, email) 
VALUES ('alice123', '[email protected]');

-- 插入时覆盖某些默认值
INSERT INTO users_system (username, email, role, email_verified) 
VALUES ('admin_bob', '[email protected]', 'admin', true);

SELECT * FROM users_system;

场景2:电商订单系统

sql
CREATE TABLE ecommerce_orders (
    -- 订单ID,自动生成
    order_id BIGSERIAL PRIMARY KEY,
    
    -- 订单号,自动生成格式化编号
    order_number text DEFAULT 'ORD' || to_char(CURRENT_DATE, 'YYYYMMDD') || 
                               lpad(nextval('order_seq')::text, 6, '0'),
    
    -- 客户ID
    customer_id integer NOT NULL,
    
    -- 订单状态,默认为待付款
    status text DEFAULT 'pending' 
           CHECK (status IN ('pending', 'paid', 'shipped', 'delivered', 'cancelled')),
    
    -- 总金额,默认为0
    total_amount numeric(10,2) DEFAULT 0.00,
    
    -- 货币,默认人民币
    currency text DEFAULT 'CNY',
    
    -- 创建时间
    created_at timestamp DEFAULT CURRENT_TIMESTAMP,
    
    -- 支付截止时间,默认创建后24小时
    payment_deadline timestamp DEFAULT (CURRENT_TIMESTAMP + INTERVAL '24 hours'),
    
    -- 备注,默认为空
    notes text DEFAULT ''
);

-- 创建订单编号序列
CREATE SEQUENCE order_seq START 1;

修改默认值

添加默认值

sql
-- 为已存在的列添加默认值
ALTER TABLE products 
ALTER COLUMN description SET DEFAULT '暂无描述';

修改默认值

sql
-- 修改现有的默认值
ALTER TABLE products 
ALTER COLUMN price SET DEFAULT 19.99;

删除默认值

sql
-- 删除列的默认值
ALTER TABLE products 
ALTER COLUMN price DROP DEFAULT;

实际示例

sql
-- 创建测试表
CREATE TABLE test_defaults (
    id SERIAL,
    name text,
    status text
);

-- 添加默认值
ALTER TABLE test_defaults 
ALTER COLUMN status SET DEFAULT 'new';

-- 插入测试数据
INSERT INTO test_defaults (name) VALUES ('测试项目');

-- 修改默认值
ALTER TABLE test_defaults 
ALTER COLUMN status SET DEFAULT 'active';

-- 再次插入数据
INSERT INTO test_defaults (name) VALUES ('另一个项目');

SELECT * FROM test_defaults;

输出:

idnamestatus
1测试项目new
2另一个项目active

最佳实践

1. 选择合适的默认值

设计原则

  • 有意义的默认值:选择在业务逻辑中最常见或最安全的值
  • 避免歧义:默认值应该清晰明确,不会造成误解
  • 考虑数据完整性:默认值应该符合列的约束条件

好的例子:

sql
CREATE TABLE good_defaults (
    status text DEFAULT 'active' CHECK (status IN ('active', 'inactive')),
    priority integer DEFAULT 1 CHECK (priority BETWEEN 1 AND 5),
    is_public boolean DEFAULT false,
    created_at timestamp DEFAULT CURRENT_TIMESTAMP
);

避免的例子:

sql
CREATE TABLE bad_defaults (
    status text DEFAULT 'unknown',  -- 模糊不清
    priority integer DEFAULT 999,   -- 可能超出合理范围
    name text DEFAULT 'temp'        -- 可能造成重复或混淆
);

2. 使用函数默认值的注意事项

sql
-- 正确:每次插入时计算
CREATE TABLE events (
    id SERIAL,
    event_time timestamp DEFAULT CURRENT_TIMESTAMP,
    event_date date DEFAULT CURRENT_DATE
);

-- 错误:不要使用不稳定的函数结果
-- 这会在每次插入时产生不同的随机值,可能不是期望的行为
CREATE TABLE problematic (
    id SERIAL,
    token text DEFAULT md5(random()::text)  -- 需要谨慎考虑
);

3. 序列号的最佳实践

sql
-- 推荐:使用 SERIAL 类型
CREATE TABLE recommended (
    id SERIAL PRIMARY KEY,
    name text NOT NULL
);

-- 或者明确创建序列(当需要更多控制时)
CREATE SEQUENCE custom_id_seq 
    START 1000 
    INCREMENT 10 
    CACHE 50;

CREATE TABLE custom_sequence (
    id integer DEFAULT nextval('custom_id_seq') PRIMARY KEY,
    name text NOT NULL
);

4. 性能考虑

性能提示

  • 简单常量默认值:性能开销最小
  • 函数调用默认值:每次插入都会执行函数,有一定开销
  • 复杂表达式默认值:开销较大,应谨慎使用
sql
-- 性能友好的默认值
CREATE TABLE performance_friendly (
    id SERIAL,
    status text DEFAULT 'new',              -- 常量,快速
    created_at timestamp DEFAULT now(),     -- 简单函数,较快
    is_active boolean DEFAULT true          -- 常量,快速
);

-- 性能开销较大的默认值
CREATE TABLE performance_heavy (
    id SERIAL,
    complex_calc numeric DEFAULT (
        SELECT avg(price) FROM products     -- 子查询,慢
    ),
    uuid_val uuid DEFAULT gen_random_uuid() -- UUID生成,中等开销
);