PostgreSQL 实战核心:登录、类型、结构与数据的增删改查指南
在日常的数据库开发中,掌握最核心的语法即可解决绝大多数需求。本文将 PostgreSQL 的操作分为连接登录、核心数据类型、结构定义(DDL)、数据操纵(DML)、外键机制以及进阶用法,为您梳理最常用、最基础的语句,帮助您快速上手并规避常见陷阱。
前篇:连接与登录
高频提示 :日常开发中,最常用的连接方式是通过命令行工具 psql 或图形界面工具(如 pgAdmin、DBeaver)。掌握命令行登录是最基础的内功。
1. 本地基本登录
bash
# 格式:psql -U 用户名 -d 数据库名 -h 主机地址 -p 端口
# 本地默认登录(使用 postgres 超级用户,连接默认 postgres 库)
psql -U postgres
2. 远程登录
bash
# 连接远程服务器的指定数据库
psql -U myuser -d my_database -h 192.168.1.100 -p 5432
# 执行后会提示输入密码
3. 退出登录
bash
# 在 psql 交互式命令行中输入以下任一命令退出
\q
# 或者
exit
避坑指南:首次安装 PostgreSQL 后,默认会创建一个名为 postgres 的操作系统用户和数据库超级用户。若无法登录或提示密码错误,需检查 pg_hba.conf 配置文件中的认证规则。
4. 补充:高频元命令速记
在日常的数据库操作中,你只需要掌握约 20% 的核心元命令,就能解决 80% 的实际工作需求。不用一开始就背熟所有的元命令,按核心需求来筛选,日常最常用的 psql 命令应该是这几个:
\l和\c:连入服务器后,先看看有哪些数据库(\l),然后连接到目标库(\c my_database)。\d和\dt:这是最高频的操作。进库后查看有哪些表(\dt),以及查看具体某张表的结构(\d table_name)。\x:当表字段很多,或者某列文本很长时,默认的表格排版会很难看。随手敲一个\x切换成竖排显示,能极大提升阅读体验。\q:干完活,退出。\?:剩下的那 80% 不常用的命令,平时不需要死记硬背。遇到忘了的,直接用\?查阅即可。
把上面这最常用的几个用熟,就能应付绝大多数场景了。
上篇:核心数据类型
在动手建表之前,必须搞懂最基础的数据类型。PostgreSQL 的数据类型极其丰富,但掌握以下几类核心类型,足以应对绝大部分业务场景。
1. 整数类型
- INT (或 INTEGER):最常用的整数类型,存储范围足够覆盖日常计数。
- BIGINT:更大的整数,适用于流水号、海量数据自增ID。
- SERIAL :伪类型,本质是自动递增的整数。在建表时用作主键,PG 会自动为其创建序列。
2. 字符串类型 - VARCHAR(n):变长字符,最大长度为 n。适合存有明确长度上限的文本(如姓名、标题)。
- TEXT:变长字符,无长度上限。在 PG 中,VARCHAR 和 TEXT 的性能几乎没有差异,因此存长文本(如文章内容、备注)首选 TEXT。
- CHAR(n) :定长字符,不足长度自动补空格。极少使用。
3. 日期时间类型 - DATE:只存日期(如 2023-10-01),不包含时间。
- TIMESTAMP:存日期加时间(如 2023-10-01 14:30:00),不带时区。日常建表最常用。
- TIMESTAMPTZ :带时区的日期时间。适用于跨时区的全球化业务系统。
4. 布尔与精确数值 - BOOLEAN :布尔类型,只能存
true、false或NULL。常用于表示状态(如是否启用、是否删除)。 - NUMERIC(p, s) :精确小数。p代表总位数,s代表小数位数。涉及金额、费率等不容许精度丢失的场景,必须用此类型(如
NUMERIC(10,2)表示最大8位整数,2位小数)。
核心记忆法:主键自增用 SERIAL,短文本用 VARCHAR,长文本用 TEXT,时间用 TIMESTAMP,金额用 NUMERIC,真假用 BOOLEAN。
中篇:结构定义操作(DDL)
针对数据库、表、列的结构操作,核心在于建、删、改、查。我们先从宏观到微观,依次梳理。
一、 数据库
高频提示 :日常开发中,数据库最常用的操作就是"创建、切换、删除",修改数据库名极少使用。
1. 增
sql
-- 最简创建数据库
CREATE DATABASE my_database;
2. 删
sql
-- 删除数据库(慎用!库内所有表和数据全部消失,需确保当前无活动连接)
DROP DATABASE my_database;
3. 改
sql
-- 修改数据库名称
ALTER DATABASE my_database RENAME TO new_database;
4. 查
sql
-- 查看当前实例下所有数据库(过滤掉系统模板库)
SELECT datname FROM pg_database WHERE datistemplate = false;
二、 表
高频提示 :表最常用的操作是"创建(带主键)、清空数据(截断)、删除"。
1. 增
sql
-- 切换到目标数据库(在 psql 命令行中使用 \c 命令)
\c my_database
-- 创建表(应用核心数据类型,必须定义主键)
CREATE TABLE student (
id SERIAL PRIMARY KEY, -- 自增主键
name VARCHAR(50) NOT NULL, -- 变长字符串
age INT, -- 整数
is_active BOOLEAN DEFAULT true, -- 布尔值,默认为真
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 日期时间,默认当前时间
);
2. 删
sql
-- 彻底删除表(结构和数据一起删除)
DROP TABLE student;
-- 仅删除所有数据,但保留表结构(比 DELETE 快,且重置序列,极常用)
TRUNCATE TABLE student RESTART IDENTITY;
3. 改
sql
-- 修改表名
ALTER TABLE student RENAME TO new_student;
4. 查
sql
-- 查看当前数据库 public 模式下的所有表
SELECT tablename FROM pg_tables WHERE schemaname = 'public';
三、 列
高频提示 :代码开发中,需求变更是常态,因此对列的"增、改"是最高频的操作。删除列需确认无依赖。
1. 增
sql
-- 向已有表中添加新列
ALTER TABLE student ADD COLUMN gender VARCHAR(2);
2. 删
sql
-- 删除不需要的列
ALTER TABLE student DROP COLUMN gender;
3. 改(两种常见需求)
sql
-- 需求A:修改列的数据类型/长度
ALTER TABLE student ALTER COLUMN name TYPE VARCHAR(100);
-- 需求B:修改列名
ALTER TABLE student RENAME COLUMN age TO student_age;
4. 查
sql
-- 查看 student 表的所有列信息
SELECT column_name, data_type, character_maximum_length, is_nullable
FROM information_schema.columns
WHERE table_name = 'student';
结构操作速记卡:
| 操作对象 | 增 | 删 | 改 | 查 |
|---|---|---|---|---|
| 数据库 | CREATE DATABASE |
DROP DATABASE |
ALTER DATABASE...RENAME TO |
SELECT FROM pg_database |
| 表 | CREATE TABLE |
DROP TABLE / TRUNCATE TABLE |
ALTER TABLE...RENAME TO |
SELECT FROM pg_tables |
| 列 | ALTER TABLE...ADD COLUMN |
ALTER TABLE...DROP COLUMN |
ALTER TABLE...ALTER COLUMN / RENAME COLUMN |
SELECT FROM information_schema |
下篇:数据操作(DML)
针对数据本身的操作,这是日常开发中使用频率最高的部分。掌握以下核心语法,足以应对绝大部分业务场景。
假设操作表 :student (id, name, age, gender, dept)
一、 增
1. 核心语法:单行/批量插入
sql
-- 推荐写法:显式指定列名(防错,即使以后表结构加列也不会报错)
INSERT INTO student (name, age, gender, dept)
VALUES ('张三', 20, '男', '计算机系');
2. PG特色语法:插入并返回数据
sql
-- 插入数据后,直接返回自增生成的 id,免去再次查询
INSERT INTO student (name, age)
VALUES ('李四', 21)
RETURNING id, name;
避坑指南:尽量少用 INSERT INTO student VALUES (...) 这种不写列名的写法,一旦表新增了字段,代码直接报错。
二、 删
1. 核心语法:条件删除
sql
-- 删除特定数据
DELETE FROM student WHERE id = 1;
2. 高频语法:清空全表
sql
-- 删除表中所有数据,速度比 DELETE 快得多,且重置序列
TRUNCATE TABLE student;
避坑指南:执行 DELETE 前,一定一定一定要检查有没有 WHERE 条件!没有条件就是灾难。
三、 改
1. 核心语法:条件修改
sql
-- 修改单个字段
UPDATE student SET age = 22 WHERE id = 1;
-- 修改多个字段(用逗号隔开)
UPDATE student SET age = 22, dept = '软件系' WHERE id = 1;
2. PG特色语法:更新并返回
sql
-- 更新数据后,直接返回更新后的记录
UPDATE student SET age = 23 WHERE id = 1 RETURNING *;
避坑指南:执行 UPDATE 前,先用 SELECT 查一下 WHERE 条件过滤出的数据对不对,确认无误再把 SELECT 改成 UPDATE。
四、 查(重点)
查询是 SQL 的灵魂,占据了大部分工作量。PostgreSQL 的查询功能极其强大,但最常用的组合如下:
1. 基础查询
sql
-- 查所有(生产环境慎用 *,影响性能)
SELECT * FROM student;
-- 查指定列(推荐)
SELECT name, age FROM student;
-- 去重查询(查看有哪些院系)
SELECT DISTINCT dept FROM student;
2. 条件查询
sql
-- 多条件组合 (AND / OR)
SELECT * FROM student WHERE dept = '计算机系' AND age > 20;
-- 范围查询 (BETWEEN / IN)
SELECT * FROM student WHERE age BETWEEN 20 AND 25;
SELECT * FROM student WHERE dept IN ('计算机系', '软件系');
-- 模糊查询 (LIKE,%代表任意多个字符,_代表单个字符)
SELECT * FROM student WHERE name LIKE '张%'; -- 查姓张的
3. 排序与限制
sql
-- 按年龄降序排列 (DESC 降序,ASC 升序-默认)
SELECT * FROM student ORDER BY age DESC;
-- PG 分页查询基础:LIMIT 限制条数, OFFSET 跳过条数
SELECT * FROM student ORDER BY age DESC LIMIT 10 OFFSET 0; -- 第1页,每页10条
4. 聚合与分组
sql
-- 统计每个院系的学生人数和平均年龄
SELECT
dept AS "院系",
COUNT(*) AS "人数",
AVG(age) AS "平均年龄"
FROM student
GROUP BY dept
HAVING COUNT(*) > 10; -- HAVING 用于过滤分组后的数据
5. 连表查询(终极核心)
现实业务中数据总在多张表,JOIN 是重中之重。掌握 INNER JOIN 和 LEFT JOIN 就够了。
假设有 score (student_id, course_id, grade) 表。
sql
-- 内连接:查有成绩的学生及其成绩(两张表必须匹配上才显示)
SELECT s.name, c.course_id, c.grade
FROM student s
INNER JOIN score c ON s.id = c.student_id;
-- 左连接:查所有学生,有成绩就显示成绩,没成绩就显示NULL(以左表为准)
SELECT s.name, c.grade
FROM student s
LEFT JOIN score c ON s.id = c.student_id;
数据操作速记卡:
| 操作 | 核心语法 | 黄金保命法则 |
|---|---|---|
| 增 | INSERT INTO ... VALUES ... RETURNING ... |
永远写上列名列表 |
| 删 | DELETE FROM ... WHERE ... |
没有 WHERE 的 DELETE 就是灾难! |
| 改 | UPDATE ... SET ... WHERE ... RETURNING ... |
没有 WHERE 的 UPDATE 就是灾难! |
| 查 | SELECT ... FROM ... JOIN ... WHERE ... GROUP BY ... ORDER BY ... LIMIT |
避免 SELECT *;JOIN 前想清楚用 INNER 还是 LEFT |
末篇:外键机制
在数据库设计中,外键是用来保证数据一致性的核心机制。理解外键不需要啃厚厚的理论,掌握它的基本概念、建删语法、以及它带来的"限制",就能搞定绝大部分日常工作。
一、 一秒钟看懂外键(概念篇)
外键就像一张"介绍信"或"户口本"。
假设有两张表:class(父表)和 student(子表)。
学生表里有一列 class_id(所属班级)。这个 class_id 不能随便瞎填,必须得在 class 表里真实存在。
- 父表(主表) :被引用的表(
class),提供主键。 - 子表(从表) :包含外键的表(
student),引用父表的主键。 - 一句话核心:子表的外键,必须在父表的主键里找得到,不能是"野指针"。
二、 外键的增删改(操作篇)
1. 增:建表时直接带上(一步到位)
sql
CREATE TABLE student (
id SERIAL PRIMARY KEY,
name VARCHAR(50),
class_id INT, -- 这个列将作为外键
-- 声明外键约束
CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES class(id)
);
2. 增:表建好后追加外键(最常用)
sql
-- 给现有的 student 表增加外键
ALTER TABLE student
ADD CONSTRAINT fk_student_class
FOREIGN KEY (class_id) REFERENCES class(id);
3. 删:解除外键绑定
sql
-- 删除外键约束(注意删的是约束名,不是列名)
ALTER TABLE student
DROP CONSTRAINT fk_student_class;
4. 查:看表上有哪些外键
sql
-- 查询 student 表的外键约束信息
SELECT conname AS constraint_name, conrelid::regclass AS table_name
FROM pg_constraint
WHERE contype = 'f' AND conrelid = 'student'::regclass;
三、 外键的"脾气"(规则篇)
外键的存在感,主要体现在你操作数据时引发的报错上。记住两条铁律即可。
铁律 1:插数据时,子表不能凭空捏造
- 现象 :往
student表插入一个class_id = 99的学生,但class表里根本没有 99 号班级。 - 结果 :报错!
INSERT语句被拒绝。 - 解决 :必须先往
class表里插入 99 号班级,再插学生。
铁律 2:删数据时,父表不能留"孤儿" - 现象 :1 号班级里有 50 个学生,你直接想把
class表里的 1 号班级删了(DELETE FROM class WHERE id = 1)。 - 结果 :报错!
DELETE语句被拒绝。 - 原因:如果班级删了,这 50 个学生就成了"孤儿",数据不一致。
- 解决:必须先把 1 号班级里的学生全删了(或转走),最后才能删班级。
四、 级联操作(进阶篇)
遇到"铁律2"那种删不掉父表数据的报错,除了手动先删子表,部分场景会用到"级联"。
如果你希望:删父表数据时,子表关联的数据自动跟着删,在建外键时可以加上级联声明。
sql
ALTER TABLE student
ADD CONSTRAINT fk_student_class
FOREIGN KEY (class_id) REFERENCES class(id)
ON DELETE CASCADE -- 当父表(班级)删除时,子表(学生)自动跟着删除
ON UPDATE CASCADE; -- 当父表(班级)ID修改时,子表(学生)ID自动跟着修改
注:加了 CASCADE 后,删 class 表的 1 号班级,student 表里 1 号班级的学生会瞬间消失,慎用。
外键速记卡:
| 操作 | 核心语法/规则 | 记忆点 |
|---|---|---|
| 加外键 | ALTER TABLE 子表 ADD CONSTRAINT 约束名 FOREIGN KEY (子列) REFERENCES 父表(父列) |
"子"引用"父" |
| 删外键 | ALTER TABLE 子表 DROP CONSTRAINT 约束名 |
删的是约束,不是列 |
| 插数据报错 | 子表插入了父表没有的 ID | 先有爹,后有儿子 |
| 删数据报错 | 父表还有子表数据引用,不让删 | 儿子还在,爹不能死 |
| 级联删除 | ON DELETE CASCADE |
爹没了,儿子跟着陪葬 |
避坑指南:在实际的互联网大厂业务开发中,为了追求高并发性能,有时会不建物理外键,而是通过业务代码逻辑来保证数据一致性。但在传统企业级应用、教学系统、小型系统中,物理外键依然是保证数据不脏的"最佳守门员"。
终篇:进阶核心机制
掌握了库表结构与数据的增删改查后,要想让数据库不仅"能跑"而且"跑得快、不出错",必须掌握以下四大核心模块。
一、 索引:查询提速的利器
高频提示 :索引就像字典的目录,没有索引时查数据需要全表扫描,加上索引后查询速度可以提升成百上千倍。但索引不是越多越好,它会拖慢写入速度并占用磁盘。
1. 增:创建索引
sql
-- 为 student 表的 name 列创建普通索引,加速按姓名查询
CREATE INDEX idx_student_name ON student (name);
2. 删:删除索引
sql
-- 删除不再需要的索引
DROP INDEX idx_student_name;
3. 查:看表上有哪些索引
sql
SELECT indexname FROM pg_indexes WHERE tablename = 'student';
核心记忆法:
- 主键自带索引:建表时设为主键的列,数据库会自动为其创建唯一索引,无需重复添加。
- 外键建议加索引:外键列通常用于连表查询,加上索引能极大提升 JOIN 性能。
- 避坑指南:不要在小表上建索引;不要在增删改极其频繁的列上建过多索引。
二、 事务控制:数据安全的底线
高频提示 :转账场景中,A扣钱和B加钱必须同时成功或同时失败。事务就是保证这一组操作不可分割的机制。
1. 开启与提交(成功)
sql
-- 开启事务
BEGIN;
-- 执行操作
UPDATE account SET balance = balance - 100 WHERE id = 'A';
UPDATE account SET balance = balance + 100 WHERE id = 'B';
-- 确认无误,提交到数据库持久化
COMMIT;
2. 回滚(失败)
sql
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 'A';
-- 发现B账户不存在,操作失败,撤销刚才的所有修改
ROLLBACK;
核心记忆法:事务的四大特性(ACID):原子性(不可分割)、一致性(数据正确)、隔离性(并发互不干扰)、持久性(提交即永久)。在应用层代码中操作数据库时,一定要合理配置事务的传播行为和隔离级别,避免脏读。
三、 视图:复杂查询的封装器
高频提示 :视图是一张"虚拟表",它本身不存数据,存的是一条复杂的 SQL 查询语句。常用于简化高频的多表联查,或做权限隔离(只暴露部分列给前端)。
1. 增:创建视图
sql
-- 将"学生表关联成绩表"的复杂查询封装成一个视图
CREATE VIEW v_student_score AS
SELECT s.id, s.name, c.course_id, c.grade
FROM student s
LEFT JOIN score c ON s.id = c.student_id;
2. 查:使用视图
sql
-- 使用起来和查普通表一模一样,极其方便
SELECT * FROM v_student_score WHERE name = '张三';
3. 改与删
sql
-- 修改视图定义
CREATE OR REPLACE VIEW v_student_score AS
SELECT s.id, s.name, c.grade FROM student s LEFT JOIN score c ON s.id = c.student_id;
-- 删除视图
DROP VIEW v_student_score;
核心记忆法 :视图是"只读"的逻辑封装,绝大多数情况下只对视图做 SELECT,不要对视图做 INSERT/UPDATE。
四、 常用内置函数:减少代码搬运
高频提示 :很多数据处理直接在 SQL 层面解决,比把数据拉到应用层处理效率高得多。
1. 空值处理(最常用)
sql
-- 如果 age 为 NULL,则返回 0
SELECT id, name, COALESCE(age, 0) AS age FROM student;
2. 字符串处理
sql
-- 拼接字符串
SELECT id, CONCAT(name, '-', dept) AS info FROM student;
-- 截取与大小写转换
SELECT UPPER(name) AS upper_name, SUBSTRING(name, 1, 1) AS first_char FROM student;
3. 日期处理
sql
-- 获取当前时间
SELECT CURRENT_TIMESTAMP;
-- 日期加减计算(查3天后、1年前)
SELECT CURRENT_DATE + INTERVAL '3 day', CURRENT_DATE - INTERVAL '1 year';
核心记忆法 :能用 COALESCE 解决的空值问题,绝不写在业务代码里的 if(x == null);能用 SQL 拼接的字符串,绝不拉回内存里 for 循环拼接。