PostgreSQL 高级用法
PostgreSQL 是最强大的开源关系型数据库,2025 年已超越 MySQL 成为开发者最常用的数据库。它不仅支持标准 SQL,还支持 JSON、全文搜索、地理信息等高级功能,是生信数据管理的理想选择。
核心知识点
| 知识点 | 说明 |
|---|
| 数据库类型 | 关系型数据库(RDBMS),支持扩展功能 |
| 最新版本 | PostgreSQL 17(2024.09 发布) |
| 许可证 | PostgreSQL License(类似 MIT,非常自由) |
| 核心优势 | ACID 事务、丰富数据类型、可扩展、标准兼容 |
| v17 亮点 | 增量备份、JSON_TABLE()、MERGE 增强、vacuum 优化 |
| 特色功能 | JSON/JSONB、全文搜索、CTE、窗口函数、分区表 |
| 市场地位 | 2025 年超越 MySQL 成为开发者最常用数据库(55%) |
安装配置
方法一:系统包管理器
# Ubuntu/Debian
sudo apt update
sudo apt install postgresql postgresql-contrib # 安装 PostgreSQL
# 启动服务
sudo systemctl start postgresql # 启动
sudo systemctl enable postgresql # 开机自启
# 切换到 postgres 用户
sudo -u postgres psql # 进入 PostgreSQL 命令行
方法二:Docker(推荐开发环境)
# Docker 运行 PostgreSQL 17
docker run -d \
--name postgres \
-p 5432:5432 \ # 端口映射
-e POSTGRES_PASSWORD=mypassword \ # 设置密码
-e POSTGRES_DB=bioinfo \ # 创建默认数据库
-v pgdata:/var/lib/postgresql/data \ # 数据持久化
postgres:17 # 使用 PG17 镜像
# 连接
psql -h localhost -U postgres -d bioinfo # 连接数据库
方法三:Conda
conda install -c conda-forge postgresql # 安装
initdb -D ./pgdata # 初始化数据目录
pg_ctl -D ./pgdata start # 启动
基本使用
1. 数据库和用户管理
-- 创建数据库
CREATE DATABASE bioinfo_db; -- 创建数据库
-- 创建用户
CREATE USER bioinfo_user WITH PASSWORD 'pass123'; -- 创建用户
-- 授权
GRANT ALL PRIVILEGES ON DATABASE bioinfo_db TO bioinfo_user; -- 授予全部权限
-- 切换数据库
\c bioinfo_db -- 连接到 bioinfo_db
-- 常用 psql 命令
\l -- 列出所有数据库
\dt -- 列出当前数据库的表
\d table_name -- 查看表结构
\q -- 退出
2. 创建表和基本操作
-- 创建样本表
CREATE TABLE samples (
id SERIAL PRIMARY KEY, -- 自增主键
sample_id VARCHAR(50) UNIQUE NOT NULL, -- 样本 ID,唯一
patient_age INTEGER CHECK (patient_age > 0), -- 年龄,约束 > 0
diagnosis VARCHAR(20) NOT NULL, -- 诊断
bmi DECIMAL(5,2), -- BMI
sequencing_date DATE, -- 测序日期
metadata JSONB, -- JSON 元数据(灵活存储)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 创建时间
);
-- 插入数据
INSERT INTO samples (sample_id, patient_age, diagnosis, bmi, metadata)
VALUES
('T2D_001', 55, 'T2D', 28.5, '{"platform": "NovaSeq", "reads": 50000000}'),
('T2D_002', 62, 'T2D', 30.1, '{"platform": "NovaSeq", "reads": 45000000}'),
('HC_001', 48, 'Healthy', 22.0, '{"platform": "HiSeq", "reads": 40000000}');
-- 查询
SELECT sample_id, diagnosis, bmi
FROM samples
WHERE diagnosis = 'T2D' AND bmi > 25
ORDER BY bmi DESC; -- 查询 BMI > 25 的 T2D 样本
高级用法
1. JSONB 操作(灵活存储非结构化数据)
-- JSONB 字段查询
SELECT sample_id, metadata->>'platform' AS platform -- 提取 JSON 字段
FROM samples
WHERE metadata->>'platform' = 'NovaSeq'; -- JSON 条件过滤
-- JSONB 包含查询
SELECT * FROM samples
WHERE metadata @> '{"platform": "NovaSeq"}'; -- 包含指定键值对
-- 更新 JSONB 字段
UPDATE samples
SET metadata = metadata || '{"qc_passed": true}' -- 合并新字段
WHERE sample_id = 'T2D_001';
-- JSONB 索引(加速查询)
CREATE INDEX idx_metadata ON samples USING GIN (metadata); -- GIN 索引
2. 窗口函数(Window Functions)
-- 计算每个诊断组内的 BMI 排名
SELECT
sample_id,
diagnosis,
bmi,
RANK() OVER (PARTITION BY diagnosis ORDER BY bmi DESC) AS bmi_rank, -- 组内排名
AVG(bmi) OVER (PARTITION BY diagnosis) AS group_avg_bmi -- 组平均值
FROM samples;
-- 累计分布
SELECT
sample_id,
bmi,
PERCENT_RANK() OVER (ORDER BY bmi) AS percentile -- 百分位排名
FROM samples;
3. CTE(公用表表达式)
-- 用 CTE 组织复杂查询
WITH high_bmi AS ( -- 定义 CTE
SELECT sample_id, bmi, diagnosis
FROM samples
WHERE bmi > 25
),
bmi_stats AS ( -- 第二个 CTE
SELECT diagnosis,
COUNT(*) AS count,
AVG(bmi) AS avg_bmi
FROM high_bmi
GROUP BY diagnosis
)
SELECT * FROM bmi_stats -- 使用 CTE
ORDER BY avg_bmi DESC;
4. 全文搜索
-- 添加全文搜索支持
ALTER TABLE samples ADD COLUMN search_vector tsvector; -- 添加搜索向量列
-- 更新搜索向量
UPDATE samples
SET search_vector = to_tsvector('english',
COALESCE(sample_id, '') || ' ' || COALESCE(diagnosis, ''));
-- 全文搜索查询
SELECT sample_id, diagnosis
FROM samples
WHERE search_vector @@ to_tsquery('english', 'T2D'); -- 搜索包含 T2D 的记录
-- 创建 GIN 索引加速搜索
CREATE INDEX idx_search ON samples USING GIN (search_vector);
5. 分区表(大数据优化)
-- 按范围分区(比如按年份分区)
CREATE TABLE measurements (
id SERIAL,
sample_id VARCHAR(50),
measurement_date DATE NOT NULL,
value DECIMAL(10,4)
) PARTITION BY RANGE (measurement_date); -- 按日期范围分区
-- 创建分区
CREATE TABLE measurements_2024 PARTITION OF measurements
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01'); -- 2024 年数据
CREATE TABLE measurements_2025 PARTITION OF measurements
FOR VALUES FROM ('2025-01-01') TO ('2026-01-01'); -- 2025 年数据
6. Python 操作(psycopg2)
import psycopg2 # 导入 PostgreSQL Python 驱动
# 连接数据库
conn = psycopg2.connect(
host="localhost",
database="bioinfo_db",
user="bioinfo_user",
password="pass123"
)
cursor = conn.cursor() # 创建游标
# 查询
cursor.execute("""
SELECT sample_id, bmi FROM samples
WHERE diagnosis = %s AND bmi > %s
""", ("T2D", 25)) # 参数化查询(防 SQL 注入)
for row in cursor.fetchall(): # 获取所有结果
print(f"样本: {row[0]}, BMI: {row[1]}")
# 插入
cursor.execute("""
INSERT INTO samples (sample_id, diagnosis, bmi)
VALUES (%s, %s, %s)
""", ("T2D_004", "T2D", 27.5))
conn.commit() # 提交事务
# 关闭
cursor.close()
conn.close()
常见报错与解决
| 报错信息 | 原因 | 解决方法 |
|---|
connection refused | PostgreSQL 未启动或端口错 | sudo systemctl start postgresql |
FATAL: password authentication failed | 密码错误 | 检查密码,或修改 pg_hba.conf |
relation does not exist | 表不存在 | 检查表名和当前 schema |
duplicate key violates unique constraint | 唯一键冲突 | 检查数据或用 ON CONFLICT 处理 |
permission denied for table | 无权限 | GRANT SELECT ON table TO user; |
deadlock detected | 事务死锁 | 优化事务顺序,减少锁定时间 |
速查表
-- ===== PostgreSQL 速查表 =====
-- psql 命令
-- \l 列出数据库
-- \c dbname 连接数据库
-- \dt 列出表
-- \d table 表结构
-- \di 列出索引
-- \q 退出
-- 常用 SQL
CREATE DATABASE db; -- 创建数据库
CREATE TABLE t (id SERIAL PRIMARY KEY, name TEXT); -- 创建表
INSERT INTO t (name) VALUES ('test'); -- 插入
SELECT * FROM t WHERE name = 'test'; -- 查询
UPDATE t SET name = 'new' WHERE id = 1; -- 更新
DELETE FROM t WHERE id = 1; -- 删除
-- JSONB
col->>'key' -- 提取文本值
col @> '{"k":"v"}' -- 包含查询
col || '{"new_key": "val"}' -- 合并
-- 窗口函数
ROW_NUMBER() OVER (ORDER BY col) -- 行号
RANK() OVER (PARTITION BY g ORDER BY v) -- 分组排名
LAG(col) OVER (ORDER BY col) -- 前一行值
-- 索引
CREATE INDEX idx ON t (col); -- B-tree 索引
CREATE INDEX idx ON t USING GIN (jsonb_col); -- JSONB 索引
CREATE INDEX idx ON t USING GIN (vector_col); -- 全文索引
-- 备份恢复
-- pg_dump dbname > backup.sql 备份
-- psql dbname < backup.sql 恢复
-- pg_basebackup -D backup_dir 物理备份
-- Python: pip install psycopg2-binary