PostgreSQL 数据类型 #
数据类型概述 #
PostgreSQL 提供了丰富的数据类型,是所有关系型数据库中最全面的。
text
┌─────────────────────────────────────────────────────────────┐
│ PostgreSQL 数据类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 数值类型 ├── 整数、自增整数、小数 │
│ 字符类型 ├── 定长、变长、无限制 │
│ 布尔类型 ─── true/false/null │
│ 日期时间 ├── date, time, timestamp, interval │
│ JSON类型 ├── json, jsonb │
│ 数组类型 ─── 任意类型的数组 │
│ 网络地址 ├── inet, cidr, macaddr │
│ 几何类型 ├── point, line, polygon │
│ UUID类型 ─── 通用唯一标识符 │
│ 其他类型 ├── XML, 货币, 位串, 范围类型 │
│ │
└─────────────────────────────────────────────────────────────┘
数值类型 #
整数类型 #
sql
-- 整数类型对比
┌──────────────┬────────┬─────────────────────────────────────────┐
│ 类型 │ 字节 │ 范围 │
├──────────────┼────────┼─────────────────────────────────────────┤
│ SMALLINT │ 2 │ -32768 到 +32767 │
│ INTEGER │ 4 │ -2147483648 到 +2147483647 │
│ BIGINT │ 8 │ -9223372036854775808 到 +9223372036854775807 │
└──────────────┴────────┴─────────────────────────────────────────┘
-- 创建表
CREATE TABLE numbers (
id SERIAL PRIMARY KEY,
small_num SMALLINT,
normal_num INTEGER,
big_num BIGINT
);
-- 插入数据
INSERT INTO numbers (small_num, normal_num, big_num) VALUES
(100, 100000, 10000000000),
(-32768, -2147483648, -9223372036854775808);
-- 超出范围会报错
-- INSERT INTO numbers (small_num) VALUES (32769); -- 错误!
自增整数(Serial) #
sql
-- 自增类型对比
┌──────────────┬───────────────────────────────────────────────┐
│ 类型 │ 说明 │
├──────────────┼───────────────────────────────────────────────┤
│ SMALLSERIAL │ 自增 smallint,范围 1 到 32767 │
│ SERIAL │ 自增 integer,范围 1 到 2147483647 │
│ BIGSERIAL │ 自增 bigint,范围 1 到 9223372036854775807 │
└──────────────┴───────────────────────────────────────────────┘
-- SERIAL 等价于
CREATE TABLE users (
id SERIAL PRIMARY KEY, -- 自动创建序列 users_id_seq
name VARCHAR(100)
);
-- 等价于
CREATE SEQUENCE users_id_seq;
CREATE TABLE users (
id INTEGER NOT NULL DEFAULT nextval('users_id_seq') PRIMARY KEY,
name VARCHAR(100)
);
ALTER SEQUENCE users_id_seq OWNED BY users.id;
-- 插入数据(id 自动生成)
INSERT INTO users (name) VALUES ('Alice');
INSERT INTO users (name) VALUES ('Bob');
-- 查看当前值
SELECT currval('users_id_seq'); -- 最近获取的值
SELECT lastval(); -- 任何序列最近获取的值
-- 重置序列
ALTER SEQUENCE users_id_seq RESTART WITH 1;
小数类型 #
sql
-- 小数类型对比
┌─────────────────┬────────┬─────────────────────────────────────┐
│ 类型 │ 字节 │ 说明 │
├─────────────────┼────────┼─────────────────────────────────────┤
│ DECIMAL(p,s) │ 可变 │ 精确小数,p精度,s小数位 │
│ NUMERIC(p,s) │ 可变 │ 同 DECIMAL │
│ REAL │ 4 │ 单精度浮点,6位有效数字 │
│ DOUBLE PRECISION│ 8 │ 双精度浮点,15位有效数字 │
└─────────────────┴────────┴─────────────────────────────────────┘
-- 精确小数(适合货币)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10, 2), -- 最多10位,小数点后2位
discount NUMERIC(5, 4) -- 最多5位,小数点后4位
);
INSERT INTO products (name, price, discount) VALUES
('Product A', 99.99, 0.1500),
('Product B', 1234.56, 0.2500);
-- 浮点数(适合科学计算)
CREATE TABLE measurements (
id SERIAL PRIMARY KEY,
temperature REAL,
pressure DOUBLE PRECISION
);
-- 注意浮点精度问题
SELECT 0.1::REAL + 0.2::REAL; -- 可能不是精确的 0.3
SELECT 0.1::DECIMAL + 0.2::DECIMAL; -- 精确的 0.3
字符类型 #
字符类型对比 #
sql
-- 字符类型对比
┌─────────────────┬─────────────────────────────────────────────┐
│ 类型 │ 说明 │
├─────────────────┼─────────────────────────────────────────────┤
│ CHAR(n) │ 定长,不足补空格,最大 1GB │
│ VARCHAR(n) │ 变长,有长度限制,最大 1GB │
│ TEXT │ 变长,无长度限制,最大 1GB │
└─────────────────┴─────────────────────────────────────────────┘
-- 创建表
CREATE TABLE texts (
id SERIAL PRIMARY KEY,
code CHAR(10), -- 固定10位,不足补空格
name VARCHAR(100), -- 最多100字符
description TEXT -- 无限制
);
INSERT INTO texts (code, name, description) VALUES
('ABC', 'Product', 'A very long description...');
-- 查看存储差异
SELECT
code,
LENGTH(code) AS code_length, -- 10 (补了空格)
name,
LENGTH(name) AS name_length -- 7
FROM texts;
-- CHAR 会自动补空格
SELECT 'ABC'::CHAR(10) = 'ABC '::CHAR(10); -- true
-- VARCHAR 和 TEXT 不会补空格
SELECT 'ABC'::VARCHAR(10) = 'ABC '::VARCHAR(10); -- false
字符串函数 #
sql
-- 常用字符串函数
SELECT
LENGTH('Hello'), -- 5
CHAR_LENGTH('Hello'), -- 5
UPPER('hello'), -- HELLO
LOWER('HELLO'), -- hello
INITCAP('hello world'), -- Hello World
CONCAT('Hello', ' ', 'World'), -- Hello World
'Hello' || ' ' || 'World', -- Hello World
SUBSTRING('Hello World', 1, 5), -- Hello
SUBSTRING('Hello World' FROM 7), -- World
LEFT('Hello', 3), -- Hel
RIGHT('Hello', 3), -- llo
TRIM(' Hello '), -- Hello
LTRIM(' Hello'), -- Hello
RTRIM('Hello '), -- Hello
REPLACE('Hello', 'l', 'L'), -- HeLLo
POSITION('World' IN 'Hello World'), -- 7
SPLIT_PART('a,b,c', ',', 2), -- b
LPAD('5', 3, '0'), -- 005
RPAD('5', 3, '0'), -- 500
REVERSE('Hello'), -- olleH
REPEAT('Ab', 3); -- AbAbAb
布尔类型 #
sql
-- 布尔类型
-- 有效值:
-- true: TRUE, 'true', 't', 'yes', 'y', '1', 'on'
-- false: FALSE, 'false', 'f', 'no', 'n', '0', 'off'
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
title VARCHAR(200),
is_completed BOOLEAN DEFAULT FALSE,
is_important BOOLEAN
);
-- 插入数据
INSERT INTO tasks (title, is_completed, is_important) VALUES
('Task 1', TRUE, 'yes'),
('Task 2', FALSE, 'no'),
('Task 3', 't', 'y'),
('Task 4', '1', '0');
-- 查询
SELECT * FROM tasks WHERE is_completed = TRUE;
SELECT * FROM tasks WHERE is_completed; -- 简写
SELECT * FROM tasks WHERE NOT is_completed; -- 未完成
-- 布尔运算
SELECT
TRUE AND FALSE, -- false
TRUE OR FALSE, -- true
NOT TRUE, -- false
TRUE IS TRUE, -- true
NULL IS TRUE, -- false
NULL IS NOT TRUE, -- true (包括 false 和 null)
TRUE IS NOT NULL; -- true
日期时间类型 #
日期时间类型对比 #
sql
-- 日期时间类型对比
┌──────────────┬────────┬─────────────────────────────────────────┐
│ 类型 │ 字节 │ 说明 │
├──────────────┼────────┼─────────────────────────────────────────┤
│ DATE │ 4 │ 日期(年月日) │
│ TIME │ 8 │ 时间(时分秒) │
│ TIMETZ │ 12 │ 带时区的时间 │
│ TIMESTAMP │ 8 │ 日期和时间 │
│ TIMESTAMPTZ │ 8 │ 带时区的日期和时间 │
│ INTERVAL │ 16 │ 时间间隔 │
└──────────────┴────────┴─────────────────────────────────────────┘
-- 创建表
CREATE TABLE events (
id SERIAL PRIMARY KEY,
event_date DATE,
event_time TIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_at_tz TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO events (event_date, event_time) VALUES
('2026-03-29', '14:30:00'),
(CURRENT_DATE, CURRENT_TIME);
日期时间函数 #
sql
-- 当前日期时间
SELECT
CURRENT_DATE, -- 2026-03-29
CURRENT_TIME, -- 14:30:00.123456+08
CURRENT_TIMESTAMP, -- 2026-03-29 14:30:00.123456+08
LOCALTIME, -- 14:30:00.123456
LOCALTIMESTAMP, -- 2026-03-29 14:30:00.123456
NOW(); -- 2026-03-29 14:30:00.123456+08
-- 日期时间运算
SELECT
CURRENT_DATE + INTERVAL '1 day',
CURRENT_DATE - INTERVAL '1 week',
CURRENT_TIMESTAMP + INTERVAL '1 month 2 days 3 hours',
CURRENT_TIMESTAMP - INTERVAL '1 year 6 months';
-- 提取部分
SELECT
EXTRACT(YEAR FROM CURRENT_TIMESTAMP), -- 2026
EXTRACT(MONTH FROM CURRENT_TIMESTAMP), -- 3
EXTRACT(DAY FROM CURRENT_TIMESTAMP), -- 29
EXTRACT(HOUR FROM CURRENT_TIMESTAMP), -- 14
EXTRACT(DOW FROM CURRENT_TIMESTAMP), -- 星期几 (0-6, 0=周日)
EXTRACT(DOY FROM CURRENT_TIMESTAMP), -- 一年中第几天
EXTRACT(WEEK FROM CURRENT_TIMESTAMP); -- 一年中第几周
-- 截断
SELECT
DATE_TRUNC('year', CURRENT_TIMESTAMP), -- 2026-01-01 00:00:00
DATE_TRUNC('month', CURRENT_TIMESTAMP), -- 2026-03-01 00:00:00
DATE_TRUNC('day', CURRENT_TIMESTAMP), -- 2026-03-29 00:00:00
DATE_TRUNC('hour', CURRENT_TIMESTAMP); -- 2026-03-29 14:00:00
-- 日期差值
SELECT
AGE(TIMESTAMP '2026-12-31', TIMESTAMP '2026-01-01'),
-- 11 mons 30 days
DATE_PART('day', TIMESTAMP '2026-12-31' - TIMESTAMP '2026-01-01');
-- 364
时间间隔(Interval) #
sql
-- 时间间隔格式
SELECT
INTERVAL '1 year',
INTERVAL '2 months',
INTERVAL '3 days',
INTERVAL '4 hours',
INTERVAL '5 minutes',
INTERVAL '6 seconds',
INTERVAL '1 year 2 months 3 days',
INTERVAL '1-2' YEAR TO MONTH, -- 1年2个月
INTERVAL '3 4:05:06' DAY TO SECOND; -- 3天4小时5分6秒
-- 实际应用
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
user_id INTEGER,
start_date DATE,
duration INTERVAL,
end_date DATE GENERATED ALWAYS AS (start_date + duration) STORED
);
INSERT INTO subscriptions (user_id, start_date, duration) VALUES
(1, '2026-01-01', INTERVAL '1 year'),
(2, '2026-03-01', INTERVAL '6 months');
JSON 类型 #
JSON vs JSONB #
sql
-- JSON 类型对比
┌─────────────┬─────────────────────────────────────────────────┐
│ 类型 │ 说明 │
├─────────────┼─────────────────────────────────────────────────┤
│ JSON │ 存储为文本,每次解析,保留原始格式 │
│ JSONB │ 存储为二进制,解析一次,支持索引,键顺序不保留 │
└─────────────┴─────────────────────────────────────────────────┘
-- 推荐:大多数情况使用 JSONB
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
profile JSONB
);
-- 插入 JSON 数据
INSERT INTO users (name, profile) VALUES
('Alice', '{"age": 25, "city": "Beijing", "skills": ["Python", "SQL"]}'),
('Bob', '{"age": 30, "city": "Shanghai", "skills": ["Java", "Go"]}');
JSON 操作符 #
sql
-- 提取值
SELECT
profile->>'age' AS age, -- 文本: "25"
profile->'age' AS age_json, -- JSON: 25
profile->'skills'->0 AS first_skill, -- JSON: "Python"
profile->'skills'->>0 AS first_skill_text; -- 文本: "Python"
-- 路径提取
SELECT profile#>'{skills,0}' FROM users; -- JSON 路径
SELECT profile#>>'{skills,0}' FROM users; -- 文本路径
-- 条件查询
SELECT * FROM users WHERE profile->>'city' = 'Beijing';
SELECT * FROM users WHERE profile->'age'::INTEGER > 25;
-- 包含检查
SELECT * FROM users WHERE profile @> '{"city": "Beijing"}';
SELECT * FROM users WHERE profile ? 'age'; -- 是否有 age 键
SELECT * FROM users WHERE profile ?| ARRAY['age', 'city']; -- 是否有任一键
SELECT * FROM users WHERE profile ?& ARRAY['age', 'city']; -- 是否有所有键
JSON 函数 #
sql
-- 构建 JSON
SELECT
json_build_object('name', 'Alice', 'age', 25),
-- {"name": "Alice", "age": 25}
json_build_array(1, 2, 3),
-- [1, 2, 3]
json_object('key1', 'value1', 'key2', 'value2');
-- {"key1": "value1", "key2": "value2"}
-- 聚合为 JSON
SELECT json_agg(name) FROM users;
-- ["Alice", "Bob"]
SELECT json_object_agg(name, profile) FROM users;
-- {"Alice": {...}, "Bob": {...}}
-- 修改 JSON
UPDATE users
SET profile = profile || '{"email": "alice@example.com"}'::jsonb;
UPDATE users
SET profile = profile - 'email'; -- 删除键
-- 展开 JSON
SELECT * FROM jsonb_each('{"a": 1, "b": 2}'::jsonb);
-- key | value
-- a | 1
-- b | 2
-- 获取键和值
SELECT
jsonb_object_keys(profile) AS keys
FROM users;
-- 设置嵌套值
UPDATE users
SET profile = jsonb_set(profile, '{skills,0}', '"JavaScript"');
数组类型 #
数组定义 #
sql
-- 定义数组列
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
tags VARCHAR(50)[], -- 字符串数组
scores INTEGER[], -- 整数数组
coordinates NUMERIC(10,2)[][] -- 二维数组
);
-- 插入数据
INSERT INTO projects (name, tags, scores) VALUES
('Project A', ARRAY['web', 'frontend', 'react'], ARRAY[90, 85, 92]),
('Project B', '{"backend", "api", "node"}', '{80, 88, 75}');
数组操作 #
sql
-- 访问元素(索引从1开始)
SELECT
name,
tags[1] AS first_tag,
scores[1] AS first_score
FROM projects;
-- 数组切片
SELECT tags[1:2] FROM projects;
-- 获取数组长度
SELECT
name,
array_length(tags, 1) AS tag_count,
cardinality(tags) AS tag_count_v2;
-- 查询包含某元素
SELECT * FROM projects WHERE 'react' = ANY(tags);
SELECT * FROM projects WHERE tags @> ARRAY['react'];
SELECT * FROM projects WHERE 'react' IN (SELECT unnest(tags));
-- 查询包含所有元素
SELECT * FROM projects WHERE tags @> ARRAY['web', 'frontend'];
-- 数组连接
SELECT ARRAY[1, 2] || ARRAY[3, 4]; -- {1, 2, 3, 4}
SELECT ARRAY[1, 2] || 3; -- {1, 2, 3}
-- 展开数组
SELECT name, unnest(tags) AS tag FROM projects;
-- 聚合为数组
SELECT array_agg(name) FROM projects;
数组函数 #
sql
-- 常用数组函数
SELECT
array_length(ARRAY[1,2,3], 1), -- 3
cardinality(ARRAY[1,2,3]), -- 3
array_append(ARRAY[1,2], 3), -- {1,2,3}
array_prepend(0, ARRAY[1,2]), -- {0,1,2}
array_remove(ARRAY[1,2,2,3], 2), -- {1,3}
array_replace(ARRAY[1,2,3], 2, 5), -- {1,5,3}
array_position(ARRAY[1,2,3], 2), -- 2
array_positions(ARRAY[1,2,2,3], 2), -- {2,3}
array_to_string(ARRAY[1,2,3], ','), -- '1,2,3'
string_to_array('a,b,c', ','); -- {a,b,c}
其他常用类型 #
UUID 类型 #
sql
-- 启用 UUID 扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 创建表
CREATE TABLE sessions (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_id INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 生成 UUID
SELECT uuid_generate_v4(); -- 随机 UUID
SELECT uuid_generate_v1(); -- 基于时间戳的 UUID
-- 直接使用 gen_random_uuid() (PostgreSQL 13+)
SELECT gen_random_uuid();
网络地址类型 #
sql
-- 网络地址类型
┌─────────────┬─────────────────────────────────────────────────┐
│ 类型 │ 说明 │
├─────────────┼─────────────────────────────────────────────────┤
│ INET │ IPv4 或 IPv6 主机地址 │
│ CIDR │ IPv4 或 IPv6 网络地址 │
│ MACADDR │ MAC 地址 │
│ MACADDR8 │ 8字节 MAC 地址 │
└─────────────┴─────────────────────────────────────────────────┘
CREATE TABLE networks (
id SERIAL PRIMARY KEY,
ip_address INET,
network CIDR,
mac_address MACADDR
);
INSERT INTO networks (ip_address, network, mac_address) VALUES
('192.168.1.100', '192.168.1.0/24', '08:00:2b:01:02:03'),
('2001:db8::1', '2001:db8::/32', '08-00-2b-01-02-03');
-- 网络操作
SELECT
ip_address,
network,
ip_address << network AS is_in_network, -- 是否在网络中
host(ip_address), -- 提取主机地址
netmask(network); -- 网络掩码
范围类型 #
sql
-- 内置范围类型
┌─────────────────┬─────────────────────────────────────────────┐
│ 类型 │ 说明 │
├─────────────────┼─────────────────────────────────────────────┤
│ INT4RANGE │ integer 范围 │
│ INT8RANGE │ bigint 范围 │
│ NUMRANGE │ numeric 范围 │
│ TSRANGE │ timestamp 范围(无时区) │
│ TSTZRANGE │ timestamptz 范围(有时区) │
│ DATERANGE │ date 范围 │
└─────────────────┴─────────────────────────────────────────────┘
CREATE TABLE reservations (
id SERIAL PRIMARY KEY,
room_id INTEGER,
during TSRANGE
);
INSERT INTO reservations (room_id, during) VALUES
(1, '[2026-03-29 14:00, 2026-03-29 18:00)'),
(2, '[2026-03-29 10:00, 2026-03-29 12:00]');
-- 范围操作
SELECT * FROM reservations
WHERE during && TSRANGE('[2026-03-29 15:00, 2026-03-29 16:00)');
-- && 表示重叠
-- 包含
SELECT * FROM reservations
WHERE during @> TIMESTAMP '2026-03-29 15:00';
类型转换 #
sql
-- 使用 ::type 语法
SELECT
'123'::INTEGER,
'2026-03-29'::DATE,
'{"a": 1}'::JSONB,
ARRAY[1, 2, 3]::VARCHAR[];
-- 使用 CAST 函数
SELECT
CAST('123' AS INTEGER),
CAST('2026-03-29' AS DATE);
-- 隐式转换
SELECT
123 || '456', -- '123456' (整数转文本)
CURRENT_DATE::TEXT; -- '2026-03-29'
学习路径 #
text
入门阶段
├── PostgreSQL简介
├── 安装与配置
├── 基础语法
└── 数据类型(本文)
基础阶段
├── 数据库操作
├── 表操作
├── 数据CRUD
└── 基础查询
下一步 #
了解了数据类型后,接下来学习 数据库操作,开始创建和管理数据库!
最后更新:2026-03-29