1 2 3 4 5 6
| 作者: 夜泊1990 企鹅: 1611756908 Q 群: 948233848 邮箱: hd1611756908@163.com 博客: https://hs-an-yue.github.io/ B 站: https://space.bilibili.com/514155929/
|
第一章 概述
第1节 介绍
PostgreSQL 是一个开源的关系型数据库,和 MySQL、Oracle 属于同一类数据库
第2节 起源
它最初源于加州大学伯克利分校的 POSTGRES 项目,目前由全球社区持续维护。
第3节 和MySQL区别
| 对比 |
PostgreSQL |
MySQL |
| 类型 |
关系型数据库 |
关系型数据库 |
| 开源 |
✅ |
✅ |
| 事务 |
✅ |
✅ |
| SQL 支持 |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐ |
| JSON |
非常强 |
很强 |
| 地理空间 |
PostGIS 非常强 |
有 GIS |
| 学习难度 |
稍高 |
相对简单 |
| 国内使用普及度 |
较高 |
很高 |
第二章 安装
第1节 官网
1
| https://www.postgresql.org/
|
第2节 安装
Windows 系统命令
1
| docker run -d --name pg18-gis -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=gis_main -e TZ=Asia/Shanghai -v pg18_postgis_data:/var/lib/postgresql --restart=always postgis/postgis:18-3.6
|
Linux系统命令
1 2 3 4 5 6 7 8 9 10 11
| docker run -d \ --name pg18-gis \ -p 5432:5432 \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=gis_main \ -e TZ=Asia/Shanghai \ -v /opt/pg18_data:/var/lib/postgresql \ --restart=always \ postgis/postgis:18-3.6
|
第3节 客户端
自带客户端(psql) - 通过容器进入
1
| docker exec -it 容器名称/容器ID psql -U postgres -d gis_main
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| psql基础命令
\? \h
\l \l+ \c test_db \conninfo
\dn \dn+ \dt \dt+ \d test_table1 \d+ test_table1 \dx \du
\r \q
|
官方提供GUI - pgAdmin4
第三方GUI - DBeaver
第三章 库介绍(MySQL vs PG)
MySQL
1 2 3 4 5 6 7
| MySQL实例(服务) ├─ Database(数据库/库) │ ├─ Table 表 │ ├─ View 视图 │ └─ 函数、存储过程
MySQL表直接放在库下面
|
PG
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| PG实例(服务) ├─ Database(数据库) │ ├─ Schema(模式,很多人翻译成“方案”) │ │ ├─ public(默认自带schema!最重要) │ │ │ └─ Table、视图、函数、空间表 │ │ ├─ tenant_01(自定义schema,做多租户) │ │ └─ tenant_02 │ └─ 其他schema
PG和MySQL不同,PG的表不是直接在数据库中,PG在库和表之间多了一层Schema
在Schema下,默认带一个public的Schema,还可以自定义多个Schema,PG的表放在Schema下进行管理
在MySQL的用户看来,PG中的Schema更像数据库。
|
第四章 SQL语句
第1节 数据库管理
PG安装好后会自带系统默认库 postgres,不要把自己的业务数据存到这个库中,针对自己的业务要新建自己的业务库。
1 2 3 4 5 6 7
| PG默认库
1. postgres(系统自带默认库) 2. template0(系统自带默认库) 3. template1(系统自带默认库)
虽然三个库都有各自的用途,但是针对使用者来说,关系不大,这里不做介绍,下面用到再说。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
最简写法: CREATE DATABASE test_db;
描述: CREATE DATABASE 创建库的关键字,test_db自定义的库名,命名见名知意即可。 不需要像MySQL那样指定编码,PG默认utf8,不像MySQL那样utf8还分为utf8mb3和utf8mb4
指定模板: CREATE DATABASE test_db TEMPLATE template0;
描述: template0:PG默认自带的库(不可修改) template1:PG默认自带的库(可以修改,但是很少有人修改) PG在创建数据库的时候必须基于template0或者template1进行创建,不指定默认是template1,上面的最简写法就是。 PG的想法很简单,你要创建一个库,不知道怎么配置这个库,官方给你两个库,你找一个根据它进行复制就行了。
完整写法: CREATE DATABASE test_db TEMPLATE template0 ENCODING 'UTF8' LC_COLLATE 'zh_CN.UTF-8' LC_CTYPE 'zh_CN.UTF-8' CONNECTION LIMIT -1;
最简写法: DROP DATABASE test_db;
安全写法: DROP DATABASE IF EXISTS test_db;
注意:⚠️
有会话连接到目标库时,无法删除,需要强制断开连接,再删除。
第一步: 强制断开连接
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'test_db' AND pid <> pg_backend_pid(); 第二步: 删除
DROP DATABASE IF EXISTS test_db;
|
第2节 数据库表管理
2.1 数据结构(常用)
| 分类 |
MySQL |
PostgreSQL |
差异说明 & 业务建议 |
| 整型 |
tinyint / smallint / int / bigint |
smallint / int / bigint |
取值范围基本一致 & 主键推荐两者都用bigint |
| 高精度小数 |
decimal(p,s) |
numeric(p,s) |
等价,无精度丢失;GPS 坐标、金额优先选用 |
| 浮点数 |
float / double |
real / double precision |
两者都存在精度丢失 |
| 字符串 |
varchar(n) / char(n) / longtext |
text(推荐,不限长) / varchar(n) / char(n) |
PG 中 text 与 varchar 性能相同,推荐 text;MySQL 大文本用 longtext 等,性能偏弱 |
| 时间类型 |
datetime(无时区) / timestamp(有 2038 上限) / date / time |
timestamp(无时区) / timestamptz(带时区) / date / time |
PG 优先 timestamptz;MySQL 业务时间推荐 datetime |
| 布尔 |
tinyint(1)(伪布尔,约定 0/1) |
boolean(原生,true/false/null) |
MySQL 布尔底层是数字,容易产生判断异常;PG 是原生布尔 |
| JSON |
json |
json(文本)jsonb(二进制,支持 GIN 索引) |
PG jsonb 能力强,支持包含、键值索引;MySQL JSON 索引能力弱 |
| 数组 |
无原生数组 |
原生支持 bigint[]、text[] |
MySQL 只能逗号分隔字符串(不可索引)场景:点位关联多张图片 ID,PG 数组更便捷 |
其他空间地理信息的数据类型,请查看官网。
2.2 创建表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| CREATE TABLE sys_dept ( dept_id BIGINT PRIMARY KEY, dept_name TEXT NOT NULL, parent_id BIGINT NOT NULL DEFAULT 0, sort INT DEFAULT 1, status INT NOT NULL DEFAULT 1, is_deleted INT DEFAULT 0, create_id BIGINT, update_id BIGINT, create_time TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP );
COMMENT ON TABLE sys_dept IS '部门表';
COMMENT ON COLUMN sys_dept.dept_id IS '部门ID,主键'; COMMENT ON COLUMN sys_dept.dept_name IS '部门名称'; COMMENT ON COLUMN sys_dept.parent_id IS '父ID,最顶层从0开始'; COMMENT ON COLUMN sys_dept.sort IS '排序序号'; COMMENT ON COLUMN sys_dept.status IS '状态(1-正常 0-停用)'; COMMENT ON COLUMN sys_dept.is_deleted IS '逻辑删除标识(1-已删除 0-未删除)'; COMMENT ON COLUMN sys_dept.create_id IS '创建人ID,关联用户表(sys_user)主键'; COMMENT ON COLUMN sys_dept.update_id IS '更新人ID,关联用户表(sys_user)主键'; COMMENT ON COLUMN sys_dept.create_time IS '创建时间'; COMMENT ON COLUMN sys_dept.update_time IS '更新时间';
CREATE INDEX idx_sys_dept_dept_name ON sys_dept(dept_name); CREATE INDEX idx_sys_dept_parent_id ON sys_dept(parent_id); CREATE INDEX idx_sys_dept_sort ON sys_dept(sort); CREATE INDEX idx_sys_dept_create_time ON sys_dept(create_time); CREATE INDEX idx_sys_dept_update_time ON sys_dept(update_time);
ALTER TABLE sys_dept ADD CONSTRAINT fk_sys_dept_create_id FOREIGN KEY (create_id) REFERENCES sys_user(user_id);
ALTER TABLE sys_dept ADD CONSTRAINT fk_sys_dept_update_id FOREIGN KEY (update_id) REFERENCES sys_user(user_id);
ALTER TABLE sys_user ADD CONSTRAINT fk_sys_user_dept_id FOREIGN KEY (dept_id) REFERENCES sys_dept(dept_id);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| CREATE TABLE sys_dept ( dept_id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, dept_name TEXT NOT NULL, parent_id BIGINT NOT NULL DEFAULT 0, sort INT DEFAULT 1, status INT NOT NULL DEFAULT 1, is_deleted INT DEFAULT 0, create_id BIGINT, update_id BIGINT, create_time TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP );
COMMENT ON TABLE sys_dept IS '部门表';
COMMENT ON COLUMN sys_dept.dept_id IS '部门ID,主键'; COMMENT ON COLUMN sys_dept.dept_name IS '部门名称'; COMMENT ON COLUMN sys_dept.parent_id IS '父ID,最顶层从0开始'; COMMENT ON COLUMN sys_dept.sort IS '排序序号'; COMMENT ON COLUMN sys_dept.status IS '状态(1-正常 0-停用)'; COMMENT ON COLUMN sys_dept.is_deleted IS '逻辑删除标识(1-已删除 0-未删除)'; COMMENT ON COLUMN sys_dept.create_id IS '创建人ID,关联用户表(sys_user)主键'; COMMENT ON COLUMN sys_dept.update_id IS '更新人ID,关联用户表(sys_user)主键'; COMMENT ON COLUMN sys_dept.create_time IS '创建时间'; COMMENT ON COLUMN sys_dept.update_time IS '更新时间';
CREATE INDEX idx_sys_dept_dept_name ON sys_dept(dept_name); CREATE INDEX idx_sys_dept_parent_id ON sys_dept(parent_id); CREATE INDEX idx_sys_dept_sort ON sys_dept(sort); CREATE INDEX idx_sys_dept_create_time ON sys_dept(create_time); CREATE INDEX idx_sys_dept_update_time ON sys_dept(update_time);
ALTER TABLE sys_dept ADD CONSTRAINT fk_sys_dept_create_id FOREIGN KEY (create_id) REFERENCES sys_user(user_id);
ALTER TABLE sys_dept ADD CONSTRAINT fk_sys_dept_update_id FOREIGN KEY (update_id) REFERENCES sys_user(user_id);
ALTER TABLE sys_user ADD CONSTRAINT fk_sys_user_dept_id FOREIGN KEY (dept_id) REFERENCES sys_dept(dept_id);
|
2.3 删除表
1 2 3 4 5
| 基础删除 DROP TABLE IF EXISTS sys_dept;
如果被其他对象依赖,级联删除(连带引用它的视图、其他表的外键约束一起删掉,谨慎使用) DROP TABLE IF EXISTS sys_dept CASCADE;
|
2.4 修改表
2.4.1 新增列
1 2 3 4 5
| ALTER TABLE sys_dept ADD COLUMN IF NOT EXISTS remark TEXT;
COMMENT ON COLUMN sys_dept.remark IS '备注';
|
2.4.2 删除列
1
| ALTER TABLE sys_dept DROP COLUMN IF EXISTS remark;
|
2.4.3 修改列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ALTER TABLE sys_dept ALTER COLUMN 字段名 TYPE 新类型;
ALTER TABLE sys_dept ALTER COLUMN 字段名 SET NOT NULL; ALTER TABLE sys_dept ALTER COLUMN 字段名 DROP NOT NULL;
ALTER TABLE sys_dept ALTER COLUMN 字段名 SET DEFAULT xxx; ALTER TABLE sys_dept ALTER COLUMN 字段名 DROP DEFAULT;
COMMENT ON COLUMN sys_dept.字段名 IS '新注释';
ALTER TABLE sys_dept RENAME COLUMN old_name TO new_name;
|
2.4.4 查询表结构
1 2
| SELECT * FROM information_schema.columns WHERE table_name='sys_dept' AND table_schema='public';
|
第3节 用户权限管理
1 2 3 4
| ALTER ROLE yaya PASSWORD 'NewPass@2026';
其他权限控制,请自行查资料
|
第五章 导入和导出
前面几章把库和表都建好了,这一章解决数据怎么进、怎么出的问题。日常就两个场景:
- 整库备份/还原 —— 用
pg_dump / pg_restore
- 单表数据导入导出 —— 用
COPY / \copy(CSV 最常用)
物理备份、主从复制属于运维范畴,本文不展开。
第1节 工具概览(MySQL vs PG)
| 用途 |
MySQL |
PostgreSQL |
| 备份单个库 |
mysqldump 库名 |
pg_dump -d 库名 |
| 还原 |
mysql 库名 < backup.sql |
psql -d 库名 -f backup.sql |
| 备份整个实例 |
mysqldump –all-databases |
pg_dumpall |
| 压缩格式备份 |
管道配合 gzip 压缩 |
pg_dump -Fc 自带压缩,配套 pg_restore 还原 |
| 表数据导出 |
SELECT … INTO OUTFILE |
\copy TO |
| 表数据导入 |
LOAD DATA INFILE |
\copy FROM |
我们的 PG 跑在 Docker 里,所以下面统一用 docker exec 方式演示(方便,不用进容器交互),容器名用第二章的 pg18-gis。
如果宿主机自己装了 PG 客户端,给命令加上 -h 127.0.0.1 -p 5432 也能直接连(密码是创建容器时设的 postgres)。还是推荐容器内执行:pg_dump 版本和数据库版本天然匹配,不会踩版本兼容的坑。
第2节 整库备份和还原(pg_dump/pg_restore)
2.1 备份
备份成 SQL 纯文本(默认格式,可读可改,跨版本通用)
1 2 3 4 5
| # 在容器里生成备份文件 docker exec pg18-gis pg_dump -U postgres -d gis_main -f /tmp/gis_main.sql
# 从容器拷回宿主机 docker cp pg18-gis:/tmp/gis_main.sql ./gis_main.sql
|
备份成自定义格式(-Fc:自带压缩,文件更小,还原更灵活,推荐做正式备份)
1 2
| docker exec pg18-gis pg_dump -U postgres -d gis_main -Fc -f /tmp/gis_main.dump docker cp pg18-gis:/tmp/gis_main.dump ./gis_main.dump
|
⚠️ Windows 下不要写成 docker exec ... > dump.sql 这种重定向导出:PowerShell 会按 UTF-16 编码写文件,导致备份文件损坏。统一用 -f 生成到容器里、再 docker cp 拷出来最稳。
只备份结构 / 只备份数据
1 2 3 4 5
| # 只导结构(建表、索引、注释等,不含数据) docker exec pg18-gis pg_dump -U postgres -d gis_main -s -f /tmp/only_ddl.sql
# 只导数据(不含建表语句) docker exec pg18-gis pg_dump -U postgres -d gis_main -a -f /tmp/only_data.sql
|
只备份某张表 / 某个 schema
1 2 3 4 5
| # 只备份 sys_dept 表 docker exec pg18-gis pg_dump -U postgres -d gis_main -t sys_dept -f /tmp/sys_dept.sql
# 只备份 public 这个 schema docker exec pg18-gis pg_dump -U postgres -d gis_main -n public -f /tmp/public.sql
|
常用参数
| 参数 |
作用 |
| -U 用户名 |
指定连接用户 |
| -d 库名 |
指定要备份的数据库 |
| -f 文件 |
输出到文件 |
| -t 表名 |
只备份指定表(可写多次,备份多张) |
| -n schema名 |
只备份指定 schema |
| -s |
只导结构(不含数据) |
| -a |
只导数据(不含结构) |
| -Fc |
自定义格式(压缩,配套 pg_restore 还原) |
| -Fd |
目录格式(超大库可用 pg_restore -j 并行还原) |
2.2 还原
⚠️ 关键点:pg_dump 备份的是库里的内容,不含建库语句(类似 mysqldump 不带 --databases 参数),所以还原前必须先手工建好空库。
还原 SQL 文本格式
1 2 3 4 5 6 7 8
| docker exec pg18-gis psql -U postgres -c "CREATE DATABASE gis_main_new"
docker cp ./gis_main.sql pg18-gis:/tmp/gis_main.sql
docker exec pg18-gis psql -U postgres -d gis_main_new -v ON_ERROR_STOP=1 -f /tmp/gis_main.sql
|
还原自定义格式(-Fc 备份的文件必须用 pg_restore 还原)
1 2 3 4 5 6 7 8 9
| # 同样先建好空库,再拷文件进容器 docker exec pg18-gis psql -U postgres -c "CREATE DATABASE gis_main_new" docker cp ./gis_main.dump pg18-gis:/tmp/gis_main.dump
# 还原 docker exec pg18-gis pg_restore -U postgres -d gis_main_new /tmp/gis_main.dump
# 如果目标库里已有同名对象,加 -c docker exec pg18-gis pg_restore -U postgres -d gis_main_new -c
|
小习惯:备份完顺手在测试库还原验证一次,避免关键时刻备份不可用。
2.3 备份整个实例(pg_dumpall)
场景:整个 PG(所有库 + 用户角色 + 权限)搬家
1 2 3 4 5 6 7
| # 备份 docker exec pg18-gis pg_dumpall -U postgres -f /tmp/pg_all.sql docker cp pg18-gis:/tmp/pg_all.sql ./pg_all.sql
# 还原(连到 postgres 库执行,脚本里自带建库、建角色语句) docker cp ./pg_all.sql pg18-gis:/tmp/pg_all.sql docker exec pg18-gis psql -U postgres -v ON_ERROR_STOP=1 -f /tmp/pg_all.sql
|
pg_dump vs pg_dumpall
| 对比 |
pg_dump |
pg_dumpall |
| 范围 |
单个库 |
整个实例(所有库+角色+权限) |
| 格式 |
SQL / 自定义 / 目录 |
只有 SQL 文本 |
| 还原方式 |
psql 或 pg_restore |
psql |
| 典型场景 |
日常单库备份 |
整机迁移、大版本升级 |
第3节 表级导入导出(COPY / \copy)
单表数据进出最常用的就是 CSV。PG 原生的 COPY 命令,比逐条 INSERT 快几个数量级,对应 MySQL 的 LOAD DATA INFILE / SELECT ... INTO OUTFILE。
同样的功能有两个版本,一句话区分:
| 命令 |
谁执行 |
文件在哪 |
权限要求 |
| COPY |
数据库服务端 |
服务器(容器)上的路径 |
需要超级用户或 pg_read_server_files / pg_write_server_files |
| \copy |
psql 客户端 |
执行 psql 的机器上的路径 |
普通用户即可 |
✅ 日常推荐用 \copy:不用超级用户,更安全,和 MySQL 的使用习惯最接近。
⚠️ 因为本文的 psql 是 docker exec 进容器执行的,所以 \copy 的文件实际也落在容器里,来回都需要 docker cp(见下面示例)。
3.1 导出 CSV
1 2 3 4 5 6 7
| # 进入 psql 后执行(进入方式见第二章)
# 最简写法:导出整表 \copy sys_dept TO '/tmp/sys_dept.csv' CSV HEADER
# 导出查询结果(可挑列、加条件) \copy (SELECT dept_id, dept_name FROM sys_dept WHERE status = 1) TO '/tmp/sys_dept_ok.csv' CSV HEADER
|
1 2
| docker cp pg18-gis:/tmp/sys_dept.csv ./sys_dept.csv
|
3.2 导入 CSV
⚠️ 前置条件:目标表必须先建好,且 CSV 中列的顺序要和表列一一对应(带表头时,HEADER 只是让导入时跳过第一行,并不会按列名自动匹配)。
1 2 3 4 5 6 7 8 9
| # 先把 CSV 拷进容器 docker cp ./sys_dept.csv pg18-gis:/tmp/sys_dept.csv
# 进入 psql 后执行 \copy sys_dept FROM '/tmp/sys_dept.csv' CSV HEADER
# 如果 CSV 列顺序与表不一致(比如 CSV 里是 名称在前、ID 在后), # 可写列清单建立对应关系 —— 列清单按 CSV 的列顺序列出表列名 \copy sys_dept (dept_name, dept_id) FROM '/tmp/sys_dept.csv' CSV HEADER
|
常用选项
| 选项 |
作用 |
| CSV |
逗号分隔(不写默认是制表符分隔) |
| HEADER |
第一行是列名,导入时自动跳过 |
| ENCODING ‘UTF8’ |
指定文件编码(中文乱码时检查这个) |
完整写法(选项都写进 WITH,更直观):
1 2
| \copy sys_dept TO '/tmp/sys_dept.csv' WITH (FORMAT csv, HEADER true, ENCODING 'UTF8') \copy sys_dept FROM '/tmp/sys_dept.csv' WITH (FORMAT csv, HEADER true, ENCODING 'UTF8')
|
3.3 带 geometry(空间字段)的表
GIS 场景的坑:COPY/\copy 不认识 WKT,不能直接往 geometry 列里导。
导出 —— geometry 列默认会导出成十六进制 EWKB,想要 WKT 得显式转换:
1
| \copy (SELECT id, name, ST_AsText(geom) FROM poi) TO '/tmp/poi.csv' CSV HEADER
|
导入 —— 不能直接进 geometry 列,分两步:先落到文本临时表,再转换入库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| CREATE TABLE poi_tmp ( id BIGINT, name TEXT, geom_text TEXT );
\copy poi_tmp FROM '/tmp/poi.csv' CSV HEADER
INSERT INTO poi (id, name, geom) SELECT id, name, ST_GeomFromText(geom_text, 4326) FROM poi_tmp;
DROP TABLE poi_tmp;
|
第4节 图形化工具(简略)
pgAdmin4
右键数据库 → Backup… / Restore…;右键表 → Import/Export Data…(本质还是调用 pg_dump 和 COPY)
DBeaver
右键表 → 导出数据 / 导入数据(支持 CSV、Excel,本质也是生成 COPY 语句)
GUI 是外壳,底层的 pg_dump / pg_restore / COPY 才是本体,命令行会用了,GUI 就是点两下的事。