Appearance
默认值 (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;
输出:
id | username | status | is_verified | created_date |
---|---|---|---|---|
1 | alice | active | false | 2024-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_id | customer_name | order_date | order_number |
---|---|---|---|
1 | 张三 | 2024-03-20 14:30:15.123 | ORD-1710934215 |
2 | 李四 | 2024-03-20 14:30:20.456 | ORD-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_no | name | price |
---|---|---|
1 | 商品A | 19.99 |
2 | 商品B | 29.99 |
3 | 商品C | 9.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 类型对比:
类型 | 范围 | 存储大小 | 等价写法 |
---|---|---|---|
SERIAL | 1 到 2,147,483,647 | 4 字节 | integer DEFAULT nextval('seq') |
BIGSERIAL | 1 到 9,223,372,036,854,775,807 | 8 字节 | bigint DEFAULT nextval('seq') |
SMALLSERIAL | 1 到 32,767 | 2 字节 | smallint DEFAULT nextval('seq') |
SERIAL 的优势
使用 SERIAL 类型时,PostgreSQL 会自动:
- 创建对应的序列对象
- 设置默认值为 nextval()
- 设置序列的所有者为该列
- 删除表时自动删除序列
高级默认值示例
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;
输出:
id | name | status |
---|---|---|
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生成,中等开销
);