第四章:数据类型详解

最后更新: 2024-01-01 作者: PostgreSQL Team
页面目录

第四章:数据类型详解

4.1 数据类型概述

PostgreSQL 提供丰富的数据类型,比大多数关系型数据库更加全面。

┌─────────────────────────────────────────────────────────────┐
│                    PostgreSQL 数据类型                        │
├─────────────────────────────────────────────────────────────┤
│  数值类型    │  字符串类型  │  日期时间类型  │  布尔类型      │
│  JSON 类型   │  数组类型    │  范围类型      │  网络地址类型  │
│  几何类型    │  位串类型    │  枚举类型      │  UUID 类型     │
│  XML 类型    │  文本搜索    │  复合类型      │  范围类型      │
└─────────────────────────────────────────────────────────────┘

4.2 数值类型

4.2.1 整数类型

类型名 存储大小 范围 说明
SMALLINT (INT2) 2 字节 -32,768 ~ 32,767 短整数
INTEGER (INT, INT4) 4 字节 -2,147,483,648 ~ 2,147,483,647 常用整数
BIGINT (INT8) 8 字节 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 长整数
SMALLSERIAL 2 字节 1 ~ 32,767 自增短整数
SERIAL 4 字节 1 ~ 2,147,483,647 自增整数
BIGSERIAL 8 字节 1 ~ 9,223,372,036,854,775,807 自增长整数
-- 创建带自增主键的表
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2) NOT NULL
);

-- 等价的 BIGSERIAL
CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    total_amount DECIMAL(12, 2) DEFAULT 0
);

4.2.2 浮点类型

类型名 存储大小 范围 说明
REAL (FLOAT4) 4 字节 6 位数字精度 单精度
DOUBLE PRECISION (FLOAT8) 8 字节 15 位数字精度 双精度
DECIMAL(p, s) 可变 用户指定 精确数值
NUMERIC(p, s) 可变 用户指定 精确数值(同 DECIMAL)
-- 创建表
CREATE TABLE measurements (
    id SERIAL PRIMARY KEY,
    temperature REAL,          -- 单精度
    humidity DOUBLE PRECISION, -- 双精度
    price NUMERIC(10, 2),       -- 10位数字,2位小数
    amount DECIMAL(15, 4)       -- 15位数字,4位小数
);

-- 插入示例
INSERT INTO measurements (temperature, humidity, price, amount)
VALUES (23.5, 65.789, 199.99, 12345.6789);

4.2.3 序列类型

-- 创建自定义序列
CREATE SEQUENCE my_sequence START 100 INCREMENT 10;

-- 使用序列
SELECT nextval('my_sequence');  -- 返回 100
SELECT nextval('my_sequence');  -- 返回 110

-- 重置序列
SELECT setval('my_sequence', 100);

-- 在表中使用序列
CREATE TABLE order_items (
    item_id BIGINT DEFAULT nextval('order_seq'),
    order_id BIGINT NOT NULL,
    product_name VARCHAR(100)
);

4.3 字符串类型

类型名 说明 最大长度
CHAR(n) 定长字符串,不足补空格 255 字节
VARCHAR(n) 变长字符串 1 GB
TEXT 变长字符串(无限制) 无限制
BPCHAR 字符类型(CHAR 别名) -
-- 创建表
CREATE TABLE user_profiles (
    id SERIAL PRIMARY KEY,
    username CHAR(20),      -- 定长 20
    nickname VARCHAR(50),   -- 最多 50 字符
    bio TEXT                 -- 无限制
);

-- 插入示例
INSERT INTO user_profiles (username, nickname, bio)
VALUES 
    ('alice', 'Alice Smith', 'Hello, I am a developer!'),
    ('bob', 'Bob Jones', NULL);

4.3.1 字符串函数

-- 拼接字符串
SELECT 'Hello' || ' ' || 'World';  -- Hello World
SELECT CONCAT('Hello', ' ', 'World');
SELECT CONCAT_WS('-', '2024', '01', '15');  -- 2024-01-15

-- 字符串长度
SELECT LENGTH('Hello');              -- 5
SELECT CHAR_LENGTH('你好');          -- 2

-- 大小写转换
SELECT UPPER('hello');  -- HELLO
SELECT LOWER('HELLO');  -- hello
SELECT INITCAP('hello world');  -- Hello World

-- 去除空格
SELECT TRIM('  hello  ');        -- hello
SELECT LTRIM('  hello  ');       -- hello  
SELECT RTRIM('  hello  ');       --   hello

-- 截取字符串
SELECT SUBSTRING('Hello World', 1, 5);  -- Hello
SELECT SUBSTR('Hello World', 7);         -- World

-- 替换
SELECT REPLACE('Hello World', 'World', 'PostgreSQL');  -- Hello PostgreSQL

-- 拆分
SELECT STRING_TO_ARRAY('a,b,c', ',');    -- {a,b,c}
SELECT ARRAY_TO_STRING(ARRAY['a','b','c'], ',');  -- a,b,c

4.4 日期时间类型

类型名 说明 范围
DATE 仅日期 4713 BC ~ 5874897 AD
TIME 仅时间 00:00:00 ~ 24:00:00
TIMESTAMP 日期时间(无时区) 4713 BC ~ 294276 AD
TIMESTAMPTZ 日期时间(有时区) 4713 BC ~ 294276 AD
INTERVAL 时间间隔 -178000000 years ~ 178000000 years
-- 创建表
CREATE TABLE events (
    id SERIAL PRIMARY KEY,
    event_name VARCHAR(100),
    event_date DATE,
    start_time TIME,
    start_at TIMESTAMP,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    duration INTERVAL
);

-- 插入示例
INSERT INTO events (event_name, event_date, start_time, start_at, duration)
VALUES 
    ('Conference', '2024-06-15', '09:00:00', '2024-06-15 09:00:00', '3 hours'),
    ('Meeting', '2024-06-16', '14:30:00', '2024-06-16 14:30:00+08', '30 minutes');

4.4.1 日期时间函数

-- 当前时间
SELECT CURRENT_DATE;           -- 当前日期
SELECT CURRENT_TIME;           -- 当前时间
SELECT CURRENT_TIMESTAMP;      -- 当前时间戳
SELECT NOW();                  -- 当前时间戳(含时区)
SELECT clock_timestamp();      -- 实时时间戳

-- 日期提取
SELECT EXTRACT(YEAR FROM CURRENT_DATE);   -- 年
SELECT EXTRACT(MONTH FROM CURRENT_DATE);  -- 月
SELECT EXTRACT(DAY FROM CURRENT_DATE);    -- 日
SELECT EXTRACT(DOW FROM CURRENT_DATE);    -- 星期 (0=周日)
SELECT EXTRACT(DOY FROM CURRENT_DATE);     -- 一年中的第几天

-- 日期计算
SELECT CURRENT_DATE + INTERVAL '7 days';   -- 7天后
SELECT CURRENT_DATE - INTERVAL '1 month';   -- 1月前
SELECT AGE('2024-06-15', '2024-01-01');    -- 时间差

-- 格式化
SELECT TO_CHAR(CURRENT_DATE, 'YYYY-MM-DD');           -- 2024-06-15
SELECT TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS');
SELECT TO_CHAR(EXTRACT(YEAR FROM CURRENT_DATE), '9999');

4.4.2 日期转换

-- 字符串转日期
SELECT '2024-06-15'::DATE;
SELECT TO_DATE('15/06/2024', 'DD/MM/YYYY');

-- 字符串转时间戳
SELECT '2024-06-15 10:30:00'::TIMESTAMP;

-- 日期转字符串
SELECT TO_CHAR(CURRENT_DATE, 'YYYY年MM月DD日');
SELECT TO_CHAR(CURRENT_DATE, 'Day, Month DD, YYYY');

-- 时间戳转日期
SELECT CURRENT_TIMESTAMP::DATE;
SELECT DATE_TRUNC('month', CURRENT_DATE);  -- 月初
SELECT DATE_TRUNC('week', CURRENT_DATE);    -- 周初

4.5 布尔类型

-- 布尔值字面量
TRUE  |  't'  |  'true'  |  'y'  |  'yes'  |  '1'
FALSE |  'f'  |  'false' |  'n'  |  'no'   |  '0'

-- 创建表
CREATE TABLE tasks (
    id SERIAL PRIMARY KEY,
    task_name VARCHAR(100),
    is_completed BOOLEAN DEFAULT false,
    is_urgent BOOLEAN DEFAULT false
);

-- 查询示例
SELECT * FROM tasks WHERE is_completed = true;
SELECT * FROM tasks WHERE NOT is_completed;
SELECT 
    task_name,
    CASE WHEN is_completed THEN '✓' ELSE '✗' END AS status
FROM tasks;

4.6 JSON 类型

PostgreSQL 支持两种 JSON 数据类型:JSON 和 JSONB。

类型 说明 特点
JSON 存储 JSON 文本 保留原始格式,不解析
JSONB 存储二进制 解析后存储,支持索引
-- 创建表
CREATE TABLE api_logs (
    id SERIAL PRIMARY KEY,
    endpoint VARCHAR(100),
    request_data JSON,
    response_data JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 插入 JSON 数据
INSERT INTO api_logs (endpoint, request_data, response_data)
VALUES 
    ('/api/users',
     '{"user_id": 1, "action": "login"}',
     '{"status": "success", "token": "abc123"}'::JSONB),
    ('/api/products',
     '{"category": "electronics", "limit": 10}',
     '{"products": [{"id": 1, "name": "Laptop"}]}');

4.6.1 JSON 操作符

-- 获取 JSON 对象字段(返回文本)
SELECT response_data->>'status' FROM api_logs;

-- 获取 JSON 对象字段(返回 JSON)
SELECT response_data->'user' FROM api_logs;

-- 获取嵌套字段
SELECT request_data->'user'->>'name' FROM api_logs;

-- 检查是否包含
SELECT * FROM api_logs WHERE request_data @> '{"user_id": 1}';
SELECT * FROM api_logs WHERE response_data ? 'status';

4.6.2 JSON 函数

-- JSON 对象创建
SELECT json_build_object('name', 'John', 'age', 30);
SELECT json_build_array('a', 'b', 'c');

-- JSON 数组展开
SELECT json_array_elements_text('["a", "b", "c"]');

-- JSON 对象键值对展开
SELECT * FROM json_each('{"name": "John", "age": 30}');

-- JSON 路径查询
SELECT * FROM api_logs 
WHERE response_data @@ '$.status == "success"';

-- JSON 路径存在检查
SELECT * FROM api_logs 
WHERE response_data @? '$.products[*] ? (@.id == 1)';

-- 行转 JSON
SELECT json_build_object('id', id, 'name', name)
FROM users WHERE id = 1;

-- JSON 合并
SELECT jsonb_merge('{"a": 1}'::JSONB, '{"b": 2}'::JSONB);

4.6.3 JSONB 索引

-- 创建 GIN 索引
CREATE INDEX idx_api_logs_response ON api_logs USING GIN (response_data);

-- 创建表达式索引
CREATE INDEX idx_api_logs_status 
ON api_logs ((response_data->>'status')) WHERE response_data ? 'status';

4.7 数组类型

PostgreSQL 原生支持数组类型,无需额外扩展。

-- 数组类型定义
CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50),
    skills TEXT[],              -- 文本数组
    phone_numbers VARCHAR(20)[], -- 定长数组
    scores INTEGER[3]            -- 3个整数的数组
);

-- 插入数组
INSERT INTO employees (name, skills, scores)
VALUES 
    ('张三', ARRAY['Python', 'PostgreSQL', 'Docker'], ARRAY[85, 90, 78]),
    ('李四', '{"Java", "Spring", "MySQL"}', '{90, 85, 92}');

4.7.1 数组操作

-- 访问数组元素(下标从 1 开始)
SELECT skills[1] FROM employees WHERE name = '张三';

-- 数组切片
SELECT scores[1:2] FROM employees WHERE name = '张三';

-- 数组包含
SELECT * FROM employees WHERE skills @> ARRAY['Python'];
SELECT * FROM employees WHERE skills && ARRAY['Java', 'Go'];

-- 数组长度
SELECT array_length(skills, 1) FROM employees;

-- 展开数组
SELECT id, unnest(skills) AS skill FROM employees;

-- 数组追加
SELECT array_append(ARRAY[1, 2], 3);  -- {1, 2, 3}

-- 数组连接
SELECT array_cat(ARRAY[1, 2], ARRAY[3, 4]);  -- {1, 2, 3, 4}

4.8 枚举类型

-- 创建枚举类型
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TYPE weekdays AS ENUM ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun');

-- 使用枚举
CREATE TABLE employee_status (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50),
    current_mood mood,
    work_day weekdays
);

-- 插入枚举值
INSERT INTO employee_status (name, current_mood, work_day)
VALUES 
    ('张三', 'happy', 'Mon'),
    ('李四', 'ok', 'Tue');

-- 枚举函数
SELECT enum_first(NULL::weekdays);   -- Mon
SELECT enum_last(NULL::weekdays);     -- Sun
SELECT enum_range(NULL::weekdays);   -- {Mon,Tue,Wed,Thu,Fri,Sat,Sun}

4.9 UUID 类型

-- 安装 UUID 扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- 创建表
CREATE TABLE sessions (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id INTEGER,
    token VARCHAR(255),
    expires_at TIMESTAMPTZ
);

-- 生成 UUID
SELECT uuid_generate_v4();           -- 随机 UUID
SELECT uuid_generate_v1();           -- 基于时间的 UUID
SELECT uuid_generate_v1mc();          -- 改进的 v1 UUID
SELECT uuid_generate_v3('namespace', 'name');  -- 命名空间 UUID

-- 常用 UUID 生成
INSERT INTO sessions (user_id, token, expires_at)
VALUES (1, gen_random_uuid()::TEXT, NOW() + INTERVAL '7 days');

4.10 网络地址类型

-- 安装网络扩展
CREATE EXTENSION IF NOT EXISTS "ip4r";

-- CIDR 类型
CREATE TABLE networks (
    id SERIAL PRIMARY KEY,
    network_name VARCHAR(50),
    ip_range CIDR
);

-- IPv4 和 IPv6
CREATE TABLE hosts (
    id SERIAL PRIMARY KEY,
    hostname VARCHAR(100),
    ip INET
);

-- 插入数据
INSERT INTO hosts (hostname, ip)
VALUES 
    ('server1', '192.168.1.100'),
    ('server2', '10.0.0.50/24'),
    ('ipv6_server', '2001:db8::1');

4.11 范围类型

-- 内置范围类型
-- int4range, int8range, numrange, tsrange, tstzrange, daterange

CREATE TABLE reservations (
    id SERIAL PRIMARY KEY,
    room_number INTEGER,
    stay_period DATERANGE,
    price_per_night NUMERIC(10, 2)
);

-- 插入范围数据
INSERT INTO reservations (room_number, stay_period, price_per_night)
VALUES 
    (101, '[2024-06-15, 2024-06-18)', 299.00),
    (102, '[2024-06-20, 2024-06-22)', 350.00);

-- 范围查询
SELECT * FROM reservations 
WHERE stay_period @> '2024-06-16'::DATE;

SELECT * FROM reservations 
WHERE stay_period && '[2024-06-14, 2024-06-16)'::DATERANGE;

4.12 域类型(自定义类型)

-- 创建域类型
CREATE DOMAIN positive_integer AS INTEGER CHECK (VALUE > 0);
CREATE DOMAIN email_address AS VARCHAR(255) 
    CHECK (VALUE ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$');
CREATE DOMAIN us_postal_code AS CHAR(5) 
    CHECK (VALUE ~ '^\d{5}$');

-- 使用域类型
CREATE TABLE contacts (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50),
    age positive_integer,
    email email_address,
    zip_code us_postal_code
);

4.13 本章小结

PostgreSQL 提供丰富的数据类型:

类别 主要类型
数值 INTEGER, BIGINT, NUMERIC, REAL
字符串 VARCHAR, TEXT, CHAR
日期时间 DATE, TIMESTAMP, TIMESTAMPTZ, INTERVAL
布尔 BOOLEAN
JSON JSON, JSONB
数组 TEXT[], INTEGER[]
枚举 自定义 ENUM
网络 INET, CIDR, MACADDR
UUID UUID

📌 下一章预告

下一章将介绍 PostgreSQL 的表设计、约束和模式管理。