PostgreSQL教程

PostgreSQL 学习教程

从基础到进阶,系统学习 PostgreSQL 数据库的核心知识与实战技巧。

目录


一、PostgreSQL 简介

1.1 什么是 PostgreSQL

PostgreSQL 是一个功能强大的开源对象关系型数据库管理系统(ORDBMS),拥有超过 35 年的活跃开发历史。它以可靠性、健壮性和扩展性著称,被称为"世界上最先进的开源数据库"。

1.2 核心特性

特性 说明
ACID 事务 完整的事务支持,保证数据一致性
MVCC 多版本并发控制,高并发读写不阻塞
丰富数据类型 原生支持 JSON、数组、UUID、网络地址、几何类型等
扩展性 支持自定义类型、函数、操作符、索引方法
外键约束 完整的参照完整性支持
递归查询 通过 CTE 支持递归 SQL
分区表 原生支持范围、列表、哈希分区
全文搜索 内置全文搜索引擎,支持中英文分词
逻辑复制 基于发布/订阅的逻辑复制
丰富生态 PostGIS(地理信息)、pgvector(向量检索)、TimescaleDB(时序)等扩展

1.3 与 MySQL 对比

维度 PostgreSQL MySQL
SQL 标准 更严格遵循 部分兼容
并发模型 MVCC 表锁 + 行锁混合
复杂查询 强(窗口函数、CTE、递归) 8.0 后逐步增强
数据类型 非常丰富 较基础
扩展能力 强(C 语言扩展、各种 Extension) 相对有限
JSON 支持 JSONB 二进制格式,性能好 5.7+ 支持,功能相对简单
适用场景 复杂业务、地理信息、数据分析 简单 CRUD、高并发读写

二、安装与配置

2.1 macOS 安装

bash 复制代码
# 使用 Homebrew 安装
brew install postgresql@16

# 启动服务
brew services start postgresql@16

# 验证
psql --version

2.2 Linux(Ubuntu/Debian)安装

bash 复制代码
# 添加官方仓库
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update

# 安装
sudo apt-get install postgresql-16

# 启动服务
sudo systemctl start postgresql
sudo systemctl enable postgresql

2.3 Docker 安装

bash 复制代码
docker run -d \
  --name postgres \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_PASSWORD=mypassword \
  -e POSTGRES_DB=mydb \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

2.4 连接数据库

bash 复制代码
# 本地连接
psql -U postgres

# 指定主机和端口
psql -h localhost -p 5432 -U myuser -d mydb

# 连接字符串方式
psql "postgresql://myuser:mypassword@localhost:5432/mydb"

2.5 核心配置参数

配置文件位置(通常在 /etc/postgresql/16/main/):

postgresql.conf --- 核心参数:

ini 复制代码
# 监听地址
listen_addresses = 'localhost'

# 端口
port = 5432

# 最大连接数
max_connections = 100

# 共享内存缓冲区(建议为系统内存的 25%)
shared_buffers = 256MB

# 有效缓存大小(建议为系统内存的 50%-75%)
effective_cache_size = 1GB

# WAL 日志相关
wal_level = replica
max_wal_size = 1GB

# 日志
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'ddl'

# 时区
timezone = 'Asia/Shanghai'

pg_hba.conf --- 客户端认证:

ini 复制代码
# TYPE  DATABASE  USER  ADDRESS       METHOD
local   all       all                 peer
host    all       all   127.0.0.1/32  scram-sha-256
host    all       all   0.0.0.0/0     scram-sha-256

三、数据库与表操作

3.1 数据库操作

sql 复制代码
-- 创建数据库
CREATE DATABASE mydb WITH ENCODING 'UTF8' LC_COLLATE='zh_CN.UTF-8' LC_CTYPE='zh_CN.UTF-8';

-- 查看所有数据库
\l

-- 切换数据库
\c mydb

-- 删除数据库
DROP DATABASE IF EXISTS mydb;

-- 修改数据库所有者
ALTER DATABASE mydb OWNER TO myuser;

3.2 Schema 操作

sql 复制代码
-- 创建 Schema
CREATE SCHEMA app;

-- 在指定 Schema 下建表
CREATE TABLE app.users (...);

-- 设置搜索路径(优先查找的 Schema)
SET search_path TO app, public;

-- 查看 Schema
\dn

3.3 创建表

sql 复制代码
CREATE TABLE users (
    id          BIGSERIAL       PRIMARY KEY,
    username    VARCHAR(64)     NOT NULL UNIQUE,
    email       VARCHAR(128)    NOT NULL,
    password    VARCHAR(256)    NOT NULL,
    nickname    VARCHAR(64),
    avatar_url  VARCHAR(512),
    status      SMALLINT        NOT NULL DEFAULT 1,
    create_time TIMESTAMPTZ     NOT NULL DEFAULT NOW(),
    update_time TIMESTAMPTZ     NOT NULL DEFAULT NOW()
);

-- 添加表注释
COMMENT ON TABLE users IS '用户表';

-- 添加列注释
COMMENT ON COLUMN users.status IS '状态:1-正常,0-禁用';

3.4 约束

sql 复制代码
CREATE TABLE orders (
    id          BIGSERIAL   PRIMARY KEY,
    user_id     BIGINT      NOT NULL,
    amount      NUMERIC(12,2) NOT NULL CHECK (amount > 0),
    status      VARCHAR(20) NOT NULL DEFAULT 'pending'
                            CHECK (status IN ('pending','paid','shipped','completed','cancelled')),
    create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- 外键约束
    CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES users(id)
);

-- 外键带级联
ALTER TABLE orders
    ADD CONSTRAINT fk_order_user
    FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE CASCADE          -- 删除用户时级联删除订单
    ON UPDATE SET NULL;        -- 更新用户ID时设为NULL

-- 唯一约束(联合唯一)
ALTER TABLE orders ADD CONSTRAINT uk_user_status UNIQUE (user_id, status);

-- 添加/删除列
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;

-- 修改列类型
ALTER TABLE users ALTER COLUMN nickname TYPE VARCHAR(128);

-- 设置默认值
ALTER TABLE users ALTER COLUMN status SET DEFAULT 1;

-- 设置非空
ALTER TABLE users ALTER COLUMN email SET NOT NULL;

-- 重命名表/列
ALTER TABLE users RENAME TO sys_user;
ALTER TABLE users RENAME COLUMN username TO login_name;

3.5 插入数据

sql 复制代码
-- 单行插入
INSERT INTO users (username, email, password, nickname)
VALUES ('zhangsan', 'zhangsan@example.com', 'hashed_pwd', '张三');

-- 多行插入
INSERT INTO users (username, email, password) VALUES
    ('lisi',     'lisi@example.com',     'hashed_pwd1'),
    ('wangwu',   'wangwu@example.com',   'hashed_pwd2'),
    ('zhaoliu',  'zhaoliu@example.com',  'hashed_pwd3');

-- 插入并返回
INSERT INTO users (username, email, password) VALUES
    ('qianqi', 'qianqi@example.com', 'hashed_pwd4')
RETURNING id, username, create_time;

-- 从查询插入
INSERT INTO user_backup (username, email, create_time)
SELECT username, email, create_time FROM users WHERE status = 1;

-- 冲突时更新(UPSERT)
INSERT INTO users (username, email, password)
VALUES ('zhangsan', 'new@example.com', 'new_pwd')
ON CONFLICT (username)
DO UPDATE SET
    email = EXCLUDED.email,
    password = EXCLUDED.password,
    update_time = NOW();

-- 冲突时忽略
INSERT INTO users (username, email, password)
VALUES ('zhangsan', 'test@example.com', 'pwd')
ON CONFLICT (username) DO NOTHING;

3.6 更新与删除

sql 复制代码
-- 更新
UPDATE users SET nickname = '张三丰', update_time = NOW() WHERE id = 1;

-- 更新并返回
UPDATE users SET status = 0 WHERE id = 1 RETURNING *;

-- 条件删除
DELETE FROM users WHERE id = 1;

-- 删除并返回
DELETE FROM users WHERE status = 0 AND create_time < '2025-01-01' RETURNING id;

3.7 清空表

sql 复制代码
-- TRUNCATE 比 DELETE FROM 更快(不逐行删除,直接清空表)
TRUNCATE TABLE orders;

-- 带级联清空(清除关联表数据)
TRUNCATE TABLE users CASCADE;

-- 重置自增序列
TRUNCATE TABLE users RESTART IDENTITY;

四、数据类型

4.1 数值类型

类型 说明 范围
SMALLINT 2字节整数 -32768 ~ 32767
INTEGER / INT 4字节整数 -21亿 ~ 21亿
BIGINT 8字节整数 非常大
SERIAL 自增 4字节整数 1 ~ 21亿
BIGSERIAL 自增 8字节整数 1 ~ 很大
NUMERIC(p,s) 精确小数 p=精度, s=小数位
REAL 4字节浮点 6位精度
DOUBLE PRECISION 8字节浮点 15位精度
MONEY 货币类型 自动格式化
sql 复制代码
CREATE TABLE products (
    id          BIGSERIAL       PRIMARY KEY,
    name        VARCHAR(128)    NOT NULL,
    price       NUMERIC(10,2)   NOT NULL,    -- 精确到分,推荐用于金额
    stock       INTEGER         NOT NULL DEFAULT 0,
    weight      REAL                        -- 浮点,允许精度损失
);

金额字段务必使用 NUMERICREAL/DOUBLE 存在精度丢失风险。

4.2 字符类型

类型 说明
CHAR(n) 定长,不足补空格
VARCHAR(n) 变长,有最大长度限制
TEXT 无长度限制的变长文本
sql 复制代码
title   VARCHAR(256)   -- 标题,限制长度
content TEXT           -- 正文,不限长度

4.3 日期时间类型

类型 说明 示例
TIMESTAMP 不带时区的时间戳 2026-03-31 10:30:00
TIMESTAMPTZ 带时区的时间戳(推荐) 2026-03-31 10:30:00+08:00
DATE 日期 2026-03-31
TIME 时间 10:30:00
INTERVAL 时间间隔 2 hours, 30 minutes
sql 复制代码
CREATE TABLE events (
    id          BIGSERIAL       PRIMARY KEY,
    name        VARCHAR(128)    NOT NULL,
    start_time  TIMESTAMPTZ     NOT NULL,
    duration    INTERVAL,
    created_at  TIMESTAMPTZ     NOT NULL DEFAULT NOW()
);

-- 插入
INSERT INTO events (name, start_time, duration) VALUES
('技术分享', '2026-03-31 14:00:00+08:00', INTERVAL '2 hours 30 minutes'),
('部门周会', '2026-04-01 09:30:00+08:00', INTERVAL '1 hour');

-- 日期时间运算
SELECT NOW() + INTERVAL '1 day';              -- 明天
SELECT NOW() - INTERVAL '3 hours';            -- 3小时前
SELECT NOW() + INTERVAL '1 month 2 days';     -- 1个月零2天后
SELECT date_trunc('month', NOW());            -- 当前月初
SELECT EXTRACT(YEAR FROM NOW());              -- 当前年份
SELECT EXTRACT(DOW FROM NOW());               -- 星期几 (0=周日)

4.4 布尔类型

sql 复制代码
CREATE TABLE articles (
    id          BIGSERIAL   PRIMARY KEY,
    title       VARCHAR(256),
    is_published BOOLEAN    NOT NULL DEFAULT FALSE,
    is_top      BOOLEAN     NOT NULL DEFAULT FALSE
);

-- 插入(多种写法)
INSERT INTO articles (title, is_published) VALUES ('测试', TRUE);
INSERT INTO articles (title, is_published) VALUES ('测试', 't');
INSERT INTO articles (title, is_published) VALUES ('测试', 'yes');
INSERT INTO articles (title, is_published) VALUES ('测试', 1);  -- 0/1 也可以

-- 查询
SELECT * FROM articles WHERE is_published = TRUE;
SELECT * FROM articles WHERE NOT is_top;

4.5 数组类型

sql 复制代码
CREATE TABLE posts (
    id      BIGSERIAL       PRIMARY KEY,
    title   VARCHAR(256)    NOT NULL,
    tags    TEXT[]           -- 文本数组
);

INSERT INTO posts (title, tags) VALUES
('PostgreSQL教程', ARRAY['数据库', 'PostgreSQL', '后端']),
('Java入门', ARRAY['Java', '编程', '后端']);

-- 查询包含特定标签的记录
SELECT * FROM posts WHERE '数据库' = ANY(tags);

-- 追加元素
UPDATE posts SET tags = array_append(tags, '入门') WHERE id = 1;

-- 数组长度
SELECT title, array_length(tags, 1) AS tag_count FROM posts;

-- 展开数组为多行
SELECT unnest(tags) AS tag FROM posts WHERE id = 1;

4.6 UUID 类型

sql 复制代码
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE files (
    id      UUID            PRIMARY KEY DEFAULT uuid_generate_v4(),
    name    VARCHAR(256)    NOT NULL,
    size    BIGINT          NOT NULL
);

INSERT INTO files (name, size) VALUES ('readme.md', 2048);
-- id 自动生成类似: 550e8400-e29b-41d4-a716-446655440000

4.7 网络地址类型

sql 复制代码
CREATE TABLE access_logs (
    id          BIGSERIAL   PRIMARY KEY,
    ip_address  INET,       -- IPv4 / IPv6
    mac_address MACADDR,    -- MAC 地址
    access_time TIMESTAMPTZ DEFAULT NOW()
);

INSERT INTO access_logs (ip_address, mac_address) VALUES
('192.168.1.100', '08:00:2b:01:02:03'),
('10.0.0.1/24',   '08-00-2b-01-02-04');

-- 包含查询
SELECT * FROM access_logs WHERE ip_address << '192.168.1.0/24';

4.8 枚举类型

sql 复制代码
-- 创建枚举类型
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'completed', 'cancelled');

CREATE TABLE orders (
    id      BIGSERIAL       PRIMARY KEY,
    status  order_status    NOT NULL DEFAULT 'pending'
);

INSERT INTO orders (status) VALUES ('paid');

-- 查看枚举值
SELECT unnest(enum_range(NULL::order_status));

4.9 SERIAL vs IDENTITY(自增主键)

sql 复制代码
-- 方式1: SERIAL(传统方式,创建序列)
CREATE TABLE t1 (id SERIAL PRIMARY KEY, name TEXT);

-- 方式2: GENERATED ALWAYS AS IDENTITY(SQL 标准,推荐)
CREATE TABLE t2 (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name TEXT
);

-- 方式3: GENERATED BY DEFAULT AS IDENTITY(可手动指定值)
CREATE TABLE t3 (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name TEXT
);

五、查询与过滤

5.1 基础查询

sql 复制代码
SELECT * FROM users;

-- 指定列
SELECT id, username, nickname FROM users;

-- 别名
SELECT id AS "用户ID", username AS "用户名", nickname AS "昵称" FROM users;

-- 去重
SELECT DISTINCT status FROM users;

-- 限制返回行数
SELECT * FROM users LIMIT 10;

-- 分页(PostgreSQL 特有语法,优于 LIMIT OFFSET)
SELECT * FROM users LIMIT 10 OFFSET 20;  -- 第3页,每页10条

5.2 条件过滤

sql 复制代码
-- WHERE 基础
SELECT * FROM users WHERE status = 1;
SELECT * FROM users WHERE age > 18 AND age < 60;
SELECT * FROM users WHERE status IN (1, 2, 3);
SELECT * FROM users WHERE nickname IS NOT NULL;
SELECT * FROM users WHERE username LIKE 'zhang%';      -- 前缀匹配
SELECT * FROM users WHERE username LIKE '%san%';        -- 包含
SELECT * FROM users WHERE username LIKE '___';          -- 精确3个字符
SELECT * FROM users WHERE username ILIKE 'ZHANG%';      -- 不区分大小写

-- 正则匹配
SELECT * FROM users WHERE username ~ '^zhang';          -- 以zhang开头
SELECT * FROM users WHERE username ~* '^ZHANG';         -- 不区分大小写
SELECT * FROM users WHERE username !~ '^test';          -- 不匹配

-- BETWEEN
SELECT * FROM users WHERE create_time BETWEEN '2026-01-01' AND '2026-03-31';

5.3 排序

sql 复制代码
SELECT * FROM users ORDER BY create_time DESC;

-- 多列排序
SELECT * FROM users ORDER BY status ASC, create_time DESC;

-- NULL 排序(NULLS FIRST / NULLS LAST)
SELECT * FROM users ORDER BY nickname NULLS LAST;

5.4 聚合函数

sql 复制代码
SELECT
    COUNT(*)            AS total_count,
    COUNT(email)        AS email_count,       -- 不统计NULL
    AVG(age)            AS avg_age,
    MAX(create_time)    AS latest_create,
    MIN(create_time)    AS earliest_create,
    SUM(order_amount)   AS total_amount
FROM users;

-- GROUP BY
SELECT status, COUNT(*) AS cnt FROM users GROUP BY status;

-- HAVING(过滤分组后的结果)
SELECT status, COUNT(*) AS cnt
FROM users
GROUP BY status
HAVING COUNT(*) > 10;

5.5 CASE 表达式

sql 复制代码
-- 简单 CASE
SELECT id, status,
    CASE status
        WHEN 1 THEN '正常'
        WHEN 0 THEN '禁用'
        ELSE '未知'
    END AS status_text
FROM users;

-- 搜索 CASE
SELECT id, age,
    CASE
        WHEN age < 18 THEN '未成年'
        WHEN age BETWEEN 18 AND 35 THEN '青年'
        WHEN age BETWEEN 36 AND 55 THEN '中年'
        ELSE '老年'
    END AS age_group
FROM users;

六、高级查询

6.1 多表连接

sql 复制代码
-- 内连接
SELECT u.username, o.id AS order_id, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;

-- 左连接
SELECT u.username, o.id AS order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;  -- 找出没有订单的用户

-- 右连接
SELECT u.username, o.amount
FROM orders o
RIGHT JOIN users u ON o.user_id = u.id;

-- 全外连接
SELECT * FROM table_a FULL OUTER JOIN table_b ON table_a.id = table_b.id;

-- 多表连接
SELECT u.username, o.amount, p.name AS product_name
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE u.status = 1;

-- 表自连接(查找同一分类下的其他产品)
SELECT p1.name, p2.name AS sibling
FROM products p1
JOIN products p2 ON p1.category_id = p2.category_id AND p1.id != p2.id;

6.2 子查询

sql 复制代码
-- WHERE 子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);

-- FROM 子查询(派生表)
SELECT status_group, cnt
FROM (
    SELECT
        CASE WHEN age < 18 THEN 'minor' ELSE 'adult' END AS status_group,
        COUNT(*) AS cnt
    FROM users
    GROUP BY status_group
) sub;

-- EXISTS 子查询(比 IN 更高效,短路求值)
SELECT u.username FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);

-- NOT EXISTS
SELECT u.username FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);

-- 关联子查询
SELECT u.username, u.email,
    (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count
FROM users u;

6.3 CTE(公共表表达式)

sql 复制代码
-- 基础 CTE
WITH active_users AS (
    SELECT * FROM users WHERE status = 1
),
user_orders AS (
    SELECT u.username, COUNT(o.id) AS order_count
    FROM active_users u
    LEFT JOIN orders o ON u.id = o.user_id
    GROUP BY u.username
)
SELECT * FROM user_orders WHERE order_count > 5;

-- 多个 CTE(用逗号分隔)
WITH
monthly_sales AS (
    SELECT date_trunc('month', create_time) AS month, SUM(amount) AS total
    FROM orders
    GROUP BY month
),
monthly_growth AS (
    SELECT month, total,
           LAG(total) OVER (ORDER BY month) AS prev_total
    FROM monthly_sales
)
SELECT month, total, prev_total,
       ROUND((total - prev_total) / prev_total * 100, 2) AS growth_rate
FROM monthly_growth;

6.4 递归查询(CTE RECURSIVE)

sql 复制代码
-- 组织架构树(经典场景)
-- 表结构:employee(id, name, manager_id)
WITH RECURSIVE org_tree AS (
    -- 基础查询:顶级节点(没有上级)
    SELECT id, name, manager_id, 1 AS level
    FROM employee
    WHERE manager_id IS NULL

    UNION ALL

    -- 递归查询:向下查找下属
    SELECT e.id, e.name, e.manager_id, t.level + 1
    FROM employee e
    INNER JOIN org_tree t ON e.manager_id = t.id
)
SELECT * FROM org_tree ORDER BY level, name;

-- 菜单树
WITH RECURSIVE menu_tree AS (
    SELECT id, name, parent_id, name AS path, 0 AS depth
    FROM menu WHERE parent_id IS NULL
    UNION ALL
    SELECT m.id, m.name, m.parent_id, mt.path || ' > ' || m.name, mt.depth + 1
    FROM menu m
    JOIN menu_tree mt ON m.parent_id = mt.id
)
SELECT * FROM menu_tree ORDER BY path;

6.5 窗口函数

sql 复制代码
-- ROW_NUMBER:行号(不重复)
SELECT id, username, create_time,
    ROW_NUMBER() OVER (ORDER BY create_time DESC) AS row_num
FROM users;

-- PARTITION BY 分组排名
SELECT department, name, salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank
FROM employees;

-- RANK / DENSE_RANK(有并列)
-- RANK:     1,2,2,4(跳号)
-- DENSE_RANK: 1,2,2,3(不跳号)
SELECT name, salary,
    RANK() OVER (ORDER BY salary DESC) AS rnk,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rnk
FROM employees;

-- LAG / LEAD(前一行/后一行)
SELECT create_time, amount,
    LAG(amount) OVER (ORDER BY create_time) AS prev_day,
    LEAD(amount) OVER (ORDER BY create_time) AS next_day
FROM daily_sales;

-- 累计求和
SELECT create_time, amount,
    SUM(amount) OVER (ORDER BY create_time) AS running_total
FROM daily_sales;

-- 移动平均(最近7天)
SELECT create_time, amount,
    AVG(amount) OVER (
        ORDER BY create_time
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS ma_7day
FROM daily_sales;

-- FIRST_VALUE / LAST_VALUE
SELECT department, name, salary,
    FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary DESC) AS top_earner
FROM employees;

6.6 UNION / INTERSECT / EXCEPT

sql 复制代码
-- UNION(去重合并)
SELECT username FROM users_a
UNION
SELECT username FROM users_b;

-- UNION ALL(不去重,更快)
SELECT username, 'table_a' AS source FROM users_a
UNION ALL
SELECT username, 'table_b' AS source FROM users_b;

-- INTERSECT(交集)
SELECT product_id FROM orders_2025_q1
INTERSECT
SELECT product_id FROM orders_2025_q2;

-- EXCEPT(差集)
SELECT product_id FROM all_products
EXCEPT
SELECT product_id FROM discontinued_products;

七、索引

7.1 索引类型

类型 说明 适用场景
B-tree 默认类型,支持等值、范围、排序 大多数查询场景
Hash 仅支持等值匹配 精确查找
GIN 倒排索引 JSON、数组、全文搜索
GiST 通用搜索树 地理数据、范围类型
BRIN 块范围索引 时间序列、超大表
SP-GiST 空间分区 GiST 特殊数据结构

7.2 创建索引

sql 复制代码
-- 普通索引
CREATE INDEX idx_users_username ON users(username);

-- 唯一索引
CREATE UNIQUE INDEX idx_users_email ON users(email);

-- 复合索引(注意列顺序,最常用列放前面)
CREATE INDEX idx_orders_user_status ON orders(user_id, status);

-- 条件索引(部分索引,减少索引体积)
CREATE INDEX idx_active_users ON users(create_time) WHERE status = 1;

-- 表达式索引
CREATE INDEX idx_users_lower_name ON users(LOWER(username));

-- 并发创建(不锁表,生产环境推荐)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);

7.3 删除与重建

sql 复制代码
-- 删除索引
DROP INDEX idx_users_username;

-- 并发删除
DROP INDEX CONCURRENTLY idx_users_username;

-- 重建索引
REINDEX INDEX idx_users_username;

-- 并发重建
REINDEX INDEX CONCURRENTLY idx_users_username;

7.4 查看索引使用情况

sql 复制代码
-- 查看表的索引
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users';

-- 查看索引大小
SELECT pg_size_pretty(pg_relation_size('idx_users_username'));

-- 查看索引使用统计(判断索引是否有效)
SELECT schemaname, relname, indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;

7.5 EXPLAIN 分析查询计划

sql 复制代码
EXPLAIN SELECT * FROM users WHERE username = 'zhangsan';
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';

关键字段解读:

  • Seq Scan --- 全表扫描(通常需要优化)
  • Index Scan --- 索引扫描
  • Bitmap Index Scan --- 位图索引扫描(适合批量查询)
  • Hash Join --- 哈希连接
  • Nested Loop --- 嵌套循环连接
  • cost=.. rows=.. --- 预估成本和行数
  • actual time=.. rows=.. --- 实际执行时间和行数

八、函数与存储过程

8.1 内置常用函数

sql 复制代码
-- 字符串
SELECT LENGTH('hello'), LEFT('hello', 3), RIGHT('hello', 2);
SELECT UPPER('hello'), LOWER('HELLO'), INITCAP('hello world');
SELECT TRIM('  hello  '), REPLACE('abc', 'b', 'x');
SELECT SUBSTRING('hello world', 7, 5);        -- world
SELECT SPLIT_PART('a,b,c,d', ',', 2);         -- b
SELECT STRING_AGG(name, ', ') FROM users;     -- 拼接

-- 数学
SELECT ROUND(3.14159, 2), CEIL(3.14), FLOOR(3.99);
SELECT ABS(-5), MOD(10, 3), POWER(2, 10);

-- 日期
SELECT NOW(), CURRENT_DATE, CURRENT_TIME;
SELECT AGE(NOW(), '2026-01-01');
SELECT DATE_PART('year', NOW());
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS');
SELECT TO_TIMESTAMP('2026-03-31 14:30:00', 'YYYY-MM-DD HH24:MI:SS');

-- NULL 处理
SELECT COALESCE(NULL, NULL, 'default');       -- default
SELECT NULLIF(10, 10);                        -- NULL(相等返回NULL)
SELECT NULLIF(10, 20);                        -- 10(不等返回原值)

8.2 自定义函数

sql 复制代码
-- PL/pgSQL 函数
CREATE OR REPLACE FUNCTION get_user_order_count(p_user_id BIGINT)
RETURNS INTEGER AS $$
DECLARE
    v_count INTEGER;
BEGIN
    SELECT COUNT(*) INTO v_count FROM orders WHERE user_id = p_user_id;
    RETURN v_count;
END;
$$ LANGUAGE plpgsql;

-- 调用
SELECT get_user_order_count(1);

-- 返回表的函数
CREATE OR REPLACE FUNCTION get_top_users(p_limit INT)
RETURNS TABLE(id BIGINT, username VARCHAR, order_count BIGINT) AS $$
BEGIN
    RETURN QUERY
    SELECT u.id, u.username, COUNT(o.id)
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    GROUP BY u.id, u.username
    ORDER BY order_count DESC
    LIMIT p_limit;
END;
$$ LANGUAGE plpgsql;

SELECT * FROM get_top_users(10);

8.3 存储过程

sql 复制代码
-- 存储过程(支持事务控制)
CREATE OR REPLACE PROCEDURE batch_update_user_status(
    p_status SMALLINT,
    p_min_orders INT
)
LANGUAGE plpgsql
AS $$
BEGIN
    -- 更新订单数超过阈值的用户状态
    UPDATE users SET status = p_status, update_time = NOW()
    WHERE id IN (
        SELECT user_id FROM orders GROUP BY user_id HAVING COUNT(*) >= p_min_orders
    );

    -- 记录日志
    RAISE NOTICE '批量更新完成,影响行数: %', ROW_COUNT;

    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE EXCEPTION '批量更新失败: %', SQLERRM;
END;
$$;

-- 调用存储过程
CALL batch_update_user_status(1, 10);

8.4 触发器

sql 复制代码
-- 创建触发器函数
CREATE OR REPLACE FUNCTION update_user_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.update_time = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 创建触发器
CREATE TRIGGER trg_user_update_time
    BEFORE UPDATE ON users
    FOR EACH ROW
    EXECUTE FUNCTION update_user_timestamp();

-- 测试:更新任意字段,update_time 会自动更新
UPDATE users SET nickname = '新昵称' WHERE id = 1;

-- 带条件的触发器
CREATE OR REPLACE FUNCTION log_order_status_change()
RETURNS TRIGGER AS $$
BEGIN
    IF OLD.status IS DISTINCT FROM NEW.status THEN
        INSERT INTO order_status_log(order_id, old_status, new_status, change_time)
        VALUES (NEW.id, OLD.status, NEW.status, NOW());
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_order_status_log
    AFTER UPDATE ON orders
    FOR EACH ROW
    EXECUTE FUNCTION log_order_status_change();

九、视图与物化视图

9.1 普通视图

sql 复制代码
-- 创建视图
CREATE VIEW v_user_order_summary AS
SELECT
    u.id AS user_id,
    u.username,
    u.nickname,
    COUNT(o.id) AS order_count,
    COALESCE(SUM(o.amount), 0) AS total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username, u.nickname;

-- 查询视图(与普通表一致)
SELECT * FROM v_user_order_summary WHERE total_amount > 1000;

-- 可更新视图(简单视图可直接更新)
CREATE VIEW v_simple_users AS
SELECT id, username, nickname FROM users WHERE status = 1;

UPDATE v_simple_users SET nickname = '新昵称' WHERE id = 1;  -- 实际更新 users 表

9.2 物化视图(数据快照,提升查询性能)

sql 复制代码
-- 创建物化视图
CREATE MATERIALIZED VIEW mv_order_daily_stats AS
SELECT
    DATE(create_time) AS order_date,
    COUNT(*) AS order_count,
    SUM(amount) AS total_amount,
    AVG(amount) AS avg_amount
FROM orders
GROUP BY DATE(create_time);

-- 手动刷新(全量刷新)
REFRESH MATERIALIZED VIEW mv_order_daily_stats;

-- 并发刷新(不阻塞查询)
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_order_daily_stats;

-- 带数据的物化视图
CREATE MATERIALIZED VIEW mv_user_stats WITH DATA AS
SELECT user_id, COUNT(*), SUM(amount)
FROM orders GROUP BY user_id;

-- 不立即填充数据
CREATE MATERIALIZED VIEW mv_user_stats WITH NO DATA AS
SELECT user_id, COUNT(*), SUM(amount)
FROM orders GROUP BY user_id;

-- 删除
DROP MATERIALIZED VIEW mv_order_daily_stats;

普通视图 vs 物化视图:普通视图每次查询都执行底层 SQL,数据实时;物化视图将结果存为物理数据,查询快但需要手动刷新。


十、事务与并发控制

10.1 事务基础

sql 复制代码
BEGIN;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

COMMIT;

-- 或回滚
ROLLBACK;

10.2 事务隔离级别

隔离级别 脏读 不可重复读 幻读 说明
Read Uncommitted 可能 可能 可能 几乎不用
Read Committed 不可能 可能 可能 PostgreSQL 默认
Repeatable Read 不可能 不可能 可能 推荐大多数场景
Serializable 不可能 不可能 不可能 最高隔离,性能最低
sql 复制代码
-- 查看当前隔离级别
SHOW transaction_isolation;

-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 会话级别设置
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;

10.3 锁机制

sql 复制代码
-- 显式加锁
SELECT * FROM users WHERE id = 1 FOR UPDATE;         -- 行级排他锁
SELECT * FROM users WHERE id = 1 FOR NO KEY UPDATE;   -- 不锁外键
SELECT * FROM users WHERE id = 1 FOR SHARE;           -- 行级共享锁

-- 表级锁
LOCK TABLE users IN ACCESS EXCLUSIVE MODE;

-- 查看当前锁
SELECT pid, relation, mode, granted
FROM pg_locks
WHERE relation = 'users'::regclass;

-- 查看阻塞
SELECT blocked.pid AS blocked_pid, blocking.pid AS blocking_pid
FROM pg_locks blocked
JOIN pg_locks blocking ON blocked.locktype = blocking.locktype
    AND blocked.database IS NOT DISTINCT FROM blocking.database
    AND blocked.relation IS NOT DISTINCT FROM blocking.relation
    AND blocked.page IS NOT DISTINCT FROM blocking.page
    AND blocked.tuple IS NOT DISTINCT FROM blocking.tuple
    AND blocked.pid != blocking.pid
WHERE NOT blocked.granted;

10.4 乐观锁(版本号方案)

sql 复制代码
CREATE TABLE products (
    id          BIGSERIAL       PRIMARY KEY,
    name        VARCHAR(128)    NOT NULL,
    stock       INTEGER         NOT NULL DEFAULT 0,
    version     INTEGER         NOT NULL DEFAULT 0   -- 版本号
);

-- 更新时检查版本号
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 1 AND version = 5;

-- 如果 affected rows = 0,说明版本号已变,需要重试

10.5 Advisory Lock(应用级锁)

sql 复制代码
-- 获取会话级排他锁(会话结束自动释放)
SELECT pg_advisory_lock(12345);

-- 尝试获取(不阻塞,返回是否成功)
SELECT pg_try_advisory_lock(12345);

-- 释放
SELECT pg_advisory_unlock(12345);

-- 事务级锁(事务结束自动释放)
SELECT pg_advisory_xact_lock(12345);

Java 中应用 :对于需要分布式锁的场景,可以用 pg_try_advisory_lock 实现基于数据库的分布式锁。


十一、JSON 支持

11.1 JSON vs JSONB

类型 存储格式 查询性能 写入性能 适用场景
JSON 原始文本 较慢 较快 需要保留原始格式
JSONB 二进制 快(支持索引) 稍慢(需转换) 推荐,大多数场景

11.2 JSONB 基本操作

sql 复制代码
CREATE TABLE configs (
    id      BIGSERIAL   PRIMARY KEY,
    key     VARCHAR(64) NOT NULL UNIQUE,
    value   JSONB       NOT NULL
);

-- 插入
INSERT INTO configs (key, value) VALUES
('site_name', '"我的网站"'),
('user_settings', '{"theme":"dark","lang":"zh","fontSize":14}'),
('feature_flags', '{"new_ui":true,"beta":false,"admin_features":["a","b","c"]}');

-- 查询(-> 返回 JSON,->> 返回文本)
SELECT value->>'theme' FROM configs WHERE key = 'user_settings';          -- dark
SELECT value->'admin_features' FROM configs WHERE key = 'feature_flags';  -- ["a","b","c"]
SELECT value->'admin_features'->>0 FROM configs WHERE key = 'feature_flags';  -- a

-- 路径查询
SELECT value #>> '{user_settings,theme}' FROM configs WHERE key = 'user_settings';

-- 条件过滤
SELECT * FROM configs WHERE value->>'theme' = 'dark';
SELECT * FROM configs WHERE value @> '{"new_ui":true}';       -- 包含
SELECT * FROM configs WHERE value ? 'admin_features';          -- 键存在
SELECT * FROM configs WHERE value ?| array['theme','lang'];    -- 任一键存在
SELECT * FROM configs WHERE value ?& array['theme','lang'];    -- 所有键存在

11.3 JSONB 修改

sql 复制代码
-- 更新整个 JSON 值
UPDATE configs SET value = '{"theme":"light","lang":"en"}' WHERE key = 'user_settings';

-- 修改某个键值
UPDATE configs SET value = jsonb_set(value, '{theme}', '"light"') WHERE key = 'user_settings';

-- 新增键
UPDATE configs SET value = value || '{"timezone":"Asia/Shanghai"}' WHERE key = 'user_settings';

-- 删除键
UPDATE configs SET value = value - 'fontSize' WHERE key = 'user_settings';

11.4 JSONB 索引

sql 复制代码
-- GIN 索引(支持 @>, ?, ?|, ?& 操作符)
CREATE INDEX idx_configs_value ON configs USING GIN (value);

-- JSONPath 索引(PG 12+)
CREATE INDEX idx_configs_value_path ON configs USING GIN (value jsonb_path_ops);

11.5 将查询结果转 JSON

sql 复制代码
-- 单行转 JSON 对象
SELECT row_to_json(u) FROM users u WHERE id = 1;

-- 多行转 JSON 数组
SELECT json_agg(row_to_json(u)) FROM users u WHERE status = 1;

-- 更精细的控制
SELECT json_build_object(
    'user_id', u.id,
    'username', u.username,
    'order_count', COALESCE(o.cnt, 0)
)
FROM users u
LEFT JOIN (SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id) o ON u.id = o.user_id;

十二、性能优化

12.1 查询优化

sql 复制代码
-- 1. 使用 EXPLAIN ANALYZE 分析慢查询
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT * FROM users WHERE email = 'test@example.com';

-- 2. 避免 SELECT *
-- 只查需要的列,减少 IO 和内存

-- 3. 避免在 WHERE 子句中对列使用函数(会导致索引失效)
-- 错误写法
SELECT * FROM users WHERE LOWER(username) = 'zhangsan';
-- 正确写法:创建表达式索引
CREATE INDEX idx_users_lower ON users(LOWER(username));

-- 4. LIKE 前缀匹配才走索引
SELECT * FROM users WHERE username LIKE 'zhang%';   -- 走索引
SELECT * FROM users WHERE username LIKE '%zhang%';   -- 不走索引,考虑全文搜索或 pg_trgm

-- 5. 使用 LIMIT 限制结果集
SELECT * FROM orders ORDER BY create_time DESC LIMIT 100;

-- 6. 批量操作优于单条操作
-- 用 INSERT ... VALUES (...), (...), (...) 代替多次 INSERT

12.2 统计信息收集

sql 复制代码
-- 手动收集统计信息(大表变更后建议执行)
ANALYZE users;

-- 收集并显示详细信息
ANALYZE VERBOSE users;

-- 查看表的统计信息
SELECT relname, n_live_tup, n_dead_tup, last_vacuum, last_autovacuum, last_analyze
FROM pg_stat_user_tables
WHERE relname = 'users';

12.3 VACUUM

sql 复制代码
-- 回收死元组空间(日常维护)
VACUUM users;

-- 回收空间并更新统计信息
VACUUM ANALYZE users;

-- 回收空间并允许操作系统回收磁盘空间
VACUUM FULL users;  -- 注意:会锁表,生产环境使用 pg_repack 代替

-- 自动清理配置(postgresql.conf)
# autovacuum = on                    -- 默认开启
# autovacuum_vacuum_scale_factor = 0.2  -- 20%行变更后触发
# autovacuum_analyze_scale_factor = 0.1

12.4 连接池

PostgreSQL 原生不支持连接池,推荐使用以下方案:

pgBouncer(推荐)

ini 复制代码
; pgbouncer.ini
[databases]
mydb = host=localhost port=5432 dbname=mydb

[pgbouncer]
pool_mode = transaction          -- 事务级池化
max_client_conn = 1000
default_pool_size = 25
reserve_pool_size = 5
reserve_pool_timeout = 3
listen_port = 6432

12.5 分区表

sql 复制代码
-- 创建分区主表
CREATE TABLE orders (
    id          BIGSERIAL,
    user_id     BIGINT      NOT NULL,
    amount      NUMERIC(12,2) NOT NULL,
    status      VARCHAR(20) NOT NULL,
    create_time TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (create_time);

-- 创建分区(按月)
CREATE TABLE orders_2026_01 PARTITION OF orders
    FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE orders_2026_02 PARTITION OF orders
    FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE orders_2026_03 PARTITION OF orders
    FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');

-- 默认分区(兜底)
CREATE TABLE orders_default PARTITION OF orders DEFAULT;

-- 查询自动路由到对应分区
SELECT * FROM orders WHERE create_time >= '2026-03-01' AND create_time < '2026-04-01';

-- 创建索引(每个分区自动创建)
CREATE INDEX idx_orders_user_id ON orders(user_id);

十三、备份与恢复

13.1 逻辑备份(pg_dump)

bash 复制代码
# 备份单个数据库(SQL格式)
pg_dump -U postgres -d mydb -f mydb_backup.sql

# 备份(自定义格式,支持并行恢复)
pg_dump -U postgres -d mydb -Fc -f mydb_backup.dump

# 备份(目录格式,支持并行备份)
pg_dump -U postgres -d mydb -Fd -j 4 -f mydb_backup_dir/

# 仅备份表结构
pg_dump -U postgres -d mydb --schema-only -f schema.sql

# 仅备份数据
pg_dump -U postgres -d mydb --data-only -f data.sql

# 备份指定表
pg_dump -U postgres -d mydb -t users -t orders -f tables.sql

# 备份所有数据库
pg_dumpall -U postgres -f all_databases.sql

13.2 恢复

bash 复制代码
# 从 SQL 文件恢复
psql -U postgres -d mydb -f mydb_backup.sql

# 从自定义格式恢复(可并行)
pg_restore -U postgres -d mydb -j 4 mydb_backup.dump

# 恢复到新数据库
createdb -U postgres newdb
pg_restore -U postgres -d newdb mydb_backup.dump

13.3 物理备份(pg_basebackup)

bash 复制代码
# 全量物理备份
pg_basebackup -h localhost -U postgres -D /data/backup/base -Ft -z -P

# 参数说明
# -D  备份目录
# -Ft tar 格式
# -z  压缩
# -P  显示进度

13.4 定时备份脚本

bash 复制代码
#!/bin/bash
# backup_pg.sh

BACKUP_DIR="/data/backups/postgresql"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="mydb"
KEEP_DAYS=7

mkdir -p $BACKUP_DIR

# 备份
pg_dump -U postgres -d $DB_NAME -Fc -f "$BACKUP_DIR/${DB_NAME}_${DATE}.dump"

# 清理过期备份
find $BACKUP_DIR -name "${DB_NAME}_*.dump" -mtime +$KEEP_DAYS -delete

echo "备份完成: ${DB_NAME}_${DATE}.dump"

十四、Java 集成

14.1 JDBC 基础

Maven 依赖:

xml 复制代码
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.7.3</version>
</dependency>

JDBC URL 格式:

复制代码
jdbc:postgresql://localhost:5432/mydb
jdbc:postgresql://localhost:5432/mydb?user=myuser&password=mypassword
jdbc:postgresql://localhost:5432/mydb?sslmode=require

14.2 Spring Boot 集成

yaml 复制代码
# application.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: myuser
    password: mypassword
    driver-class-name: org.postgresql.Driver
  jpa:
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
    show-sql: false
    open-in-view: false

14.3 MyBatis 集成

yaml 复制代码
# application.yml
mybatis:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    jdbc-type-for-null: NULL
    default-fetch-size: 100

MyBatis 中使用 PostgreSQL 特性:

xml 复制代码
<!-- 批量插入(ON CONFLICT 处理冲突) -->
<insert id="batchInsert" parameterType="list">
    INSERT INTO users (username, email, password, create_time)
    VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.username}, #{user.email}, #{user.password}, NOW())
    </foreach>
    ON CONFLICT (username) DO NOTHING
</insert>

<!-- JSONB 查询 -->
<select id="findByJsonField" resultType="User">
    SELECT * FROM users
    WHERE settings @> #{filterJson}::jsonb
</select>

<!-- 窗口函数 -->
<select id="getUserRank" resultType="UserRank">
    SELECT id, username, score,
           ROW_NUMBER() OVER (ORDER BY score DESC) AS rank
    FROM users
</select>

14.4 连接池配置(HikariCP)

yaml 复制代码
spring:
  datasource:
    hikari:
      minimum-idle: 5
      maximum-pool-size: 20
      idle-timeout: 300000
      max-lifetime: 1800000
      connection-timeout: 30000
      pool-name: MyPgPool

十五、实用 SQL 速查

15.1 数据库运维

sql 复制代码
-- 查看所有数据库及大小
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database ORDER BY pg_database_size(datname) DESC;

-- 查看当前连接数
SELECT count(*) FROM pg_stat_activity;

-- 查看活跃连接
SELECT pid, usename, datname, client_addr, state, query
FROM pg_stat_activity WHERE state = 'active';

-- 查看每个表的行数和大小
SELECT relname AS table_name,
       n_live_tup AS row_count,
       pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC;

-- 终止连接
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid = 12345;

-- 查看数据库版本
SELECT version();

15.2 表结构查询

sql 复制代码
-- 查看表结构
\d users
DESCRIBE users;

-- 查看所有列信息
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'users';

-- 查看索引
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users';

-- 查看约束
SELECT conname, contype, pg_get_constraintdef(oid) AS definition
FROM pg_constraint
WHERE conrelid = 'users'::regclass;

-- 查看表大小(含索引)
SELECT pg_size_pretty(pg_total_relation_size('users'));

-- 查看表大小(不含索引)
SELECT pg_size_pretty(pg_relation_size('users'));

-- 查看索引大小
SELECT pg_size_pretty(pg_indexes_size('users'));

15.3 常用运维操作

sql 复制代码
-- 创建只读用户
CREATE ROLE readonly_user WITH LOGIN PASSWORD 'readonly_pass';
GRANT CONNECT ON DATABASE mydb TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;
-- 对未来新建的表也生效
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_user;

-- 重置序列(表清空后重置自增ID)
SELECT setval('users_id_seq', (SELECT COALESCE(MAX(id), 1) FROM users), false);

-- 查看长事务
SELECT pid, now() - xact_start AS duration, query, state
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'active')
AND now() - xact_start > interval '5 minutes';

-- 查看表膨胀(需要 pgstattuple 扩展)
CREATE EXTENSION IF NOT EXISTS pgstattuple;
SELECT * FROM pgstattuple('users');

15.4 常用扩展

sql 复制代码
-- UUID 生成
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- 模糊匹配扩展(提升 LIKE '%xxx%' 性能)
CREATE EXTENSION IF NOT EXISTS pg_trgm;

-- 表统计信息
CREATE EXTENSION IF NOT EXISTS pgstattuple;

-- 全文搜索中文分词(需额外安装 zhparser)
CREATE EXTENSION IF NOT EXISTS zhparser;

-- 地理信息
CREATE EXTENSION IF NOT EXISTS postgis;

-- 向量检索(AI 向量搜索)
CREATE EXTENSION IF NOT EXISTS vector;

-- 超级用户才能创建扩展,普通用户需授权
GRANT ALL ON SCHEMA public TO myuser;

附录:学习资源

相关推荐
周杰伦的稻香2 小时前
PostgreSQL 16.3中复制槽的配置
数据库·postgresql
独断万古他化2 小时前
本地缓存与Redis缓存详解:区别、优缺点及场景选型
数据库·redis·缓存
Thomas.Sir2 小时前
第八章:RAG知识库开发之【Dify 实现数据库数据智能查询系统:从零构建企业级自然语言查询助手】
数据库·python·ai·dify
这辈子谁会真的心疼你2 小时前
怎么修改pdf文档属性?介绍三个方法
数据库·pdf·c#
ccice012 小时前
MySQL 函数
数据库·mysql
高梦轩8 小时前
MySQL高可用
android·运维·数据库
紫金修道10 小时前
【DeepAgent】概述
开发语言·数据库·python
孟章豪11 小时前
《SQL拼接 vs 参数化,为什么公司禁止拼接SQL?(附真实案例)》
服务器·数据库·sql
荒川之神11 小时前
ORACLE LEVEL函数练习
数据库·oracle