PostgreSQL 常用配置与工具
psql 常用命令
bash
# 连接数据库
psql -U username -d database -h hostname -p 5432
# 执行 SQL 文件
psql -U username -d database -f query.sql
# 导出数据
psql -U username -d database -c "COPY (SELECT * FROM users) TO STDOUT WITH CSV HEADER" > users.csv
# 导入数据
psql -U username -d database -c "COPY users FROM STDIN WITH CSV HEADER" < users.csv
# 查看表大小
psql -U username -d database -c "\dt+"
# 查看索引
psql -U username -d database -c "\di"
# 查看函数
psql -U username -d database -c "\df"
# 查看视图
psql -U username -d database -c "\dv"
# 执行命令
psql -U username -d database -c "SELECT version();"psql 内部命令
sql
-- 列出所有数据库
\l
\l+
-- 切换数据库
\c database_name
-- 列出所有表
\dt
\dt+
-- 列出所有索引
\di
-- 列出所有视图
\dv
-- 列出所有函数
\df
\df+
-- 查看表结构
\d table_name
\d+ table_name
-- 查看索引详情
\d index_name
-- 查看序列
\ds
-- 查看用户和权限
\du
\dp
-- 查看表空间
\db
-- 列出所有类型
\dT
-- 查看编码
\encoding
-- 查看变量
\show all
-- 执行外部文件
\i filename.sql
-- 将查询结果输出到文件
\o output.txt
SELECT * FROM users;
\o
-- 格式化输出
\x -- 切换扩展显示模式(行列转换)
\a -- 切换对齐模式
\t -- 只显示数据
\H -- HTML 格式输出
-- 启用计时
\timing
-- 编辑函数/视图
\ef function_name
\ev view_name
-- 查看历史命令
\s
-- 清空屏幕
\! cls (Windows)
\! clear (Linux/Mac)常用 SQL 模板
分页查询
sql
-- MySQL 风格(不推荐大数据量)
SELECT * FROM users LIMIT 10 OFFSET 20;
-- Keyset 分页(推荐)
SELECT * FROM users
WHERE id > 20
ORDER BY id
LIMIT 10;
-- 完整分页查询
WITH page_data AS (
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20
),
total_count AS (
SELECT COUNT(*) AS total FROM users
)
SELECT p.*, t.total FROM page_data p, total_count t;树形结构查询
sql
-- 邻接表模型
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
parent_id INTEGER REFERENCES categories(id)
);
-- 查询所有祖先(向上)
WITH RECURSIVE ancestors AS (
SELECT id, name, parent_id, 0 AS level
FROM categories WHERE id = 5
UNION ALL
SELECT c.id, c.name, c.parent_id, a.level + 1
FROM categories c
JOIN ancestors a ON c.id = a.parent_id
)
SELECT * FROM ancestors ORDER BY level;
-- 查询所有后代(向下)
WITH RECURSIVE descendants AS (
SELECT id, name, parent_id, 0 AS level
FROM categories WHERE id = 1
UNION ALL
SELECT c.id, c.name, c.parent_id, d.level + 1
FROM categories c
JOIN descendants d ON c.parent_id = d.id
)
SELECT * FROM descendants ORDER BY level;数据透视
sql
-- 静态透视
SELECT
status,
COUNT(*) AS count,
SUM(CASE WHEN age < 30 THEN 1 ELSE 0 END) AS young,
SUM(CASE WHEN age >= 30 AND age < 60 THEN 1 ELSE 0 END) AS middle,
SUM(CASE WHEN age >= 60 THEN 1 ELSE 0 END) AS old
FROM users
GROUP BY status;
-- 动态透视(使用 crosstab)
CREATE EXTENSION IF NOT EXISTS tablefunc;
SELECT * FROM crosstab(
'SELECT status, age_group, COUNT(*)
FROM (SELECT status,
CASE WHEN age < 30 THEN ''young''
WHEN age < 60 THEN ''middle''
ELSE ''old'' END AS age_group
FROM users) t
GROUP BY status, age_group
ORDER BY status',
'VALUES (''young''), (''middle''), (''old'')'
) AS ct(status VARCHAR, young BIGINT, middle BIGINT, old BIGINT);数据去重
sql
-- 删除重复数据(保留 id 最大的)
DELETE FROM users u1
USING users u2
WHERE u1.id < u2.id
AND u1.email = u2.email;
-- 使用 DISTINCT ON
SELECT DISTINCT ON (email) * FROM users ORDER BY email, created_at DESC;
-- 查找重复数据
SELECT email, COUNT(*)
FROM users
GROUP BY email
HAVING COUNT(*) > 1;合并数据
sql
-- 合并多个表的数据
SELECT id, name, 'A' AS source FROM table_a
UNION ALL
SELECT id, name, 'B' AS source FROM table_b;
-- 合并并去重
SELECT id, name FROM table_a
UNION
SELECT id, name FROM table_b;
-- 交集
SELECT id, name FROM table_a
INTERSECT
SELECT id, name FROM table_b;
-- 差集
SELECT id, name FROM table_a
EXCEPT
SELECT id, name FROM table_b;备份与恢复
逻辑备份
bash
# 导出整个数据库
pg_dump -U postgres -d mydb -F c -b -v -f mydb.dump
# 导出单个表
pg_dump -U postgres -d mydb -t users -f users.sql
# 导出数据(纯 SQL)
pg_dump -U postgres -d mydb --data-only -f data.sql
# 导出 schema(不含数据)
pg_dump -U postgres -d mydb --schema-only -f schema.sql
# 导出所有数据库(需要 superuser)
pg_dumpall -U postgres -f all_databases.sql逻辑恢复
bash
# 恢复整个数据库
pg_restore -U postgres -d mydb -v mydb.dump
# 恢复单个表
psql -U postgres -d mydb -f users.sql
# 清空数据后恢复
pg_restore -U postgres -d mydb --clean -v mydb.dump
# 仅恢复数据
pg_restore -U postgres -d mydb --data-only -v mydb.dump
# 从 pg_dumpall 恢复
psql -U postgres -f all_databases.sql物理备份
bash
# 创建基础备份(需要配置 archive_mode)
pg_basebackup -U postgres -D /backup/base -Ft -z -P -v
# 恢复基础备份
rm -rf $PGDATA
mkdir -p $PGDATA
tar -xf /backup/base.tar.gz -C $PGDATA
pg_resetwal -f $PGDATA
pg_ctl start常用管理查询
sql
-- 查看数据库大小
SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname))
FROM pg_database ORDER BY pg_database_size(pg_database.datname) DESC;
-- 查看表大小(包含索引)
SELECT schemaname, tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size,
pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) AS table_size,
pg_size_pretty(pg_indexes_size(schemaname||'.'||tablename)) AS index_size
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- 查看最慢的查询
SELECT
query,
calls,
mean_exec_time,
total_exec_time,
rows
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
-- 查看连接数
SELECT
state,
COUNT(*)
FROM pg_stat_activity
GROUP BY state;
-- 查看当前执行的查询
SELECT
pid,
now() - query_start AS duration,
state,
query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;
-- 取消正在执行的查询
SELECT pg_cancel_backend(pid); -- 温和取消
SELECT pg_terminate_backend(pid); -- 强制终止
-- 查看锁
SELECT
l.locktype,
l.relation::regclass,
l.mode,
l.granted,
l.virtualxid,
l.transactionid,
l.pid,
a.usename,
a.query
FROM pg_locks l
LEFT JOIN pg_stat_activity a ON l.pid = a.pid
WHERE NOT l.granted
OR l.pid IN (SELECT pid FROM pg_stat_activity WHERE wait_event_type = 'Lock');
-- 杀死阻塞进程
SELECT pg_terminate_backend(blocker.pid)
FROM pg_stat_activity blocker
JOIN pg_stat_activity victim ON blocker.pid = ANY(ARRAY[
SELECT l.pid FROM pg_locks l
JOIN pg_stat_activity a ON l.pid = a.pid
WHERE NOT l.granted
])
WHERE blocker.pid != pg_backend_pid();
-- 查看表膨胀率
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size,
n_dead_tup,
n_live_tup,
ROUND(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2) AS dead_ratio
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;常用函数速查
字符串函数
sql
-- 字符串操作
SELECT 'Hello' || ' ' || 'World'; -- 连接
SELECT CONCAT('Hello', ' ', 'World'); -- 连接
SELECT LENGTH('Hello'); -- 长度
SELECT UPPER('hello'); -- 转大写
SELECT LOWER('HELLO'); -- 转小写
SELECT TRIM(' hello '); -- 去空格
SELECT LTRIM(' hello'); -- 去左空格
SELECT RTRIM('hello '); -- 去右空格
SELECT SUBSTRING('Hello World', 1, 5); -- 截取
SELECT REPLACE('Hello World', 'World', 'PostgreSQL'); -- 替换
SELECT SPLIT_PART('a,b,c', ',', 2); -- 分割取第N部分
SELECT POSITION('World' IN 'Hello World'); -- 查找位置
SELECT REVERSE('abc'); -- 反转
-- 字符串格式化
SELECT FORMAT('%s, %s', 'Hello', 'World');
SELECT FORMAT('ID: %s, Name: %s', 1, '张三');日期时间函数
sql
-- 当前时间
SELECT NOW(); -- 当前时间戳
SELECT CURRENT_DATE; -- 当前日期
SELECT CURRENT_TIME; -- 当前时间
SELECT CURRENT_TIMESTAMP; -- 当前时间戳
-- 日期提取
SELECT EXTRACT(YEAR FROM NOW()); -- 年
SELECT EXTRACT(MONTH FROM NOW()); -- 月
SELECT EXTRACT(DAY FROM NOW()); -- 日
SELECT EXTRACT(DOW FROM NOW()); -- 周几(0=周日)
SELECT EXTRACT(DOY FROM NOW()); -- 年第几天
-- 日期计算
SELECT NOW() + INTERVAL '1 day'; -- 加一天
SELECT NOW() - INTERVAL '1 week'; -- 减一周
SELECT AGE(NOW(), '2024-01-01'); -- 计算年龄/差值
SELECT DATE_TRUNC('month', NOW()); -- 截断到月
-- 日期格式化
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS');
SELECT TO_CHAR(NOW(), 'YYYY年MM月DD日');
SELECT TO_DATE('20240101', 'YYYYMMDD');聚合函数
sql
SELECT COUNT(*); -- 总行数
SELECT COUNT(DISTINCT column); -- 去重计数
SELECT SUM(column); -- 求和
SELECT AVG(column); -- 平均值
SELECT MIN(column); -- 最小值
SELECT MAX(column); -- 最大值
SELECT STRING_AGG(column, ','); -- 字符串聚合
SELECT ARRAY_AGG(column); -- 数组聚合
SELECT JSON_AGG(column); -- JSON 聚合
SELECT JSONB_AGG(column); -- JSONB 聚合
SELECT XML_AGG(column); -- XML 聚合条件表达式
sql
-- CASE
SELECT CASE
WHEN age < 18 THEN '未成年'
WHEN age < 30 THEN '青年'
WHEN age < 60 THEN '中年'
ELSE '老年'
END FROM users;
-- COALESCE(返回第一个非 NULL)
SELECT COALESCE(NULL, NULL, 'default', 'value'); -- 'default'
-- NULLIF(NULL 则返回 NULL,否则返回第一个参数)
SELECT NULLIF(0, 0); -- NULL
SELECT NULLIF('a', 'b'); -- 'a'
-- GREATEST(返回最大值)
SELECT GREATEST(1, 5, 3); -- 5
-- LEAST(返回最小值)
SELECT LEAST(1, 5, 3); -- 1