2829 字
14 分钟
PostgreSQL(PGSQL)从零开始完整教程(小白也能看懂)

PostgreSQL(PGSQL)从零开始完整教程(小白也能看懂)#

上一篇我们写了 Go 服务器,能返回写死在代码里的数据。但真实的网站数据(用户、订单、文章)必须存到数据库里——关掉服务器数据也不能丢。

这一篇学 PostgreSQL(简称 PG 或 PGSQL)——目前最流行的开源数据库之一,也是很多大厂后端的主力。


一、数据库是什么?用”仓库”理解#

想象一个大型仓库(数据库),里面有好多货架(表),每个货架放一种商品(数据),每件商品有编号(主键)。

  • 数据库(Database):整个仓库
  • 表(Table):货架,比如”用户表”、“订单表”
  • 行(Row):一条数据,比如一个用户
  • 列(Column):一个字段,比如”用户名”、“年龄”
用户表(users)
┌────┬──────────┬─────┐
│ id │ name │ age │ ← 列(字段)
├────┼──────────┼─────┤
│ 1 │ 小明 │ 18 │ ← 行(一条数据)
│ 2 │ 小红 │ 17 │
└────┴──────────┴─────┘

SQL(结构化查询语言)就是和数据库对话的语言:SELECT(查)、INSERT(增)、UPDATE(改)、DELETE(删)。任何数据库(PG、MySQL、SQLite)都基本通用。


二、PostgreSQL vs MySQL:怎么选?#

对比PostgreSQLMySQL
特点功能强大、标准严格、支持复杂查询轻量、普及率高、上手快
性能复杂查询更强简单读写很快
场景复杂业务、数据分析、金融中小网站、经典 Web 应用
免费

选哪个? 学数据库建议直接学 PostgreSQL——它更现代、功能更全,而且学会 PG 语法,MySQL 基本无痛迁移(反过来不一定)。我们网站(Halo2 + Astro)也用的是 PG。


三、安装 PostgreSQL#

macOS#

Terminal window
brew install postgresql@16

Ubuntu / Debian#

Terminal window
sudo apt update
sudo apt install postgresql postgresql-contrib

Windows#

去 postgresql.org 下载安装包,一路下一步(记住安装时设置的密码)。

启动服务#

Terminal window
# macOS
brew services start postgresql@16
# Ubuntu
sudo systemctl start postgresql

四、连接数据库:psql 命令行#

PostgreSQL 装好后,默认有一个超级用户 postgres。打开终端连接:

Terminal window
sudo -u postgres psql # Ubuntu 下用这个
# 或
psql -U postgres -h localhost # 然后输入密码

看到类似 postgres=# 的提示符,就说明连上了。

常用管理命令(psql 里执行)#

-- 查看所有数据库
\l
-- 查看当前数据库所有表
\dt
-- 退出
\q

创建数据库#

CREATE DATABASE myapp;

注意: SQL 命令以分号 ; 结尾,不写分号命令不会执行。这是新手最常见的坑。

连接某个数据库#

Terminal window
\c myapp

或者直接连接:

Terminal window
psql -U postgres -d myapp

五、建表:CREATE TABLE#

表要先定义好”有哪些列、每列什么类型”。用上一篇文章的用户例子:

CREATE TABLE users (
id SERIAL PRIMARY KEY, -- 自增主键(1、2、3 自动生成)
name VARCHAR(50) NOT NULL, -- 姓名,最长50字符,不能为空
age INT CHECK (age >= 0), -- 年龄,且必须大于等于0
email VARCHAR(100) UNIQUE, -- 邮箱,不能重复
created_at TIMESTAMP DEFAULT NOW() -- 创建时间,默认当前时间
);

常用类型速查:

类型说明例子
SERIAL自增整数(主键常用)1, 2, 3…
INT整数18, -5
BIGINT大整数9000000000
NUMERIC(10,2)小数(10位整数+2位小数)99.99
VARCHAR(n)变长字符串”小明”
TEXT长文本文章内容
BOOLEAN布尔true/false
DATE日期2026-08-11
TIMESTAMP日期时间2026-08-11 09:30:00
JSONBJSON 数据(PG 特色){“key”: “value”}

主键(PRIMARY KEY):每行的唯一编号,不能重复、不能为空。就像身份证号。


六、增删改查:CRUD 四大金刚#

1. 插入数据:INSERT#

INSERT INTO users (name, age, email) VALUES ('小明', 18, 'xiaoming@example.com');
-- 插入多条
INSERT INTO users (name, age, email) VALUES
('小红', 17, 'xiaohong@example.com'),
('小刚', 20, 'xiaogang@example.com');

id 和 created_at 不用写——id 自动生成,created_at 有默认值。

2. 查询数据:SELECT#

-- 查所有列所有行(开发时常用,线上慎用)
SELECT * FROM users;
-- 只查指定列
SELECT name, age FROM users;
-- 加条件:WHERE
SELECT * FROM users WHERE age >= 18;
-- 精确匹配
SELECT * FROM users WHERE name = '小明';
-- 多个条件:AND / OR
SELECT * FROM users WHERE age >= 18 AND email LIKE '%example.com';

LIKE 模糊查询: % 表示任意字符,LIKE '%example.com' 意思是”结尾是 example.com”。

3. 更新数据:UPDATE#

-- 把 id=1 的用户的年龄改成 19
UPDATE users SET age = 19 WHERE id = 1;
-- 改多个字段
UPDATE users SET age = 20, email = 'new@example.com' WHERE id = 1;

⚠️ 最重要的一条安全规则:UPDATE 和 DELETE 一定要带 WHERE!

UPDATE users SET age = 19; -- 危险!没有 WHERE,会把所有用户的年龄都改成19

4. 删除数据:DELETE#

-- 删除 id=2 的用户
DELETE FROM users WHERE id = 2;
-- 清空整张表(保留表结构)
TRUNCATE TABLE users;

七、排序、分页、去重#

-- 按年龄从小到大排序
SELECT * FROM users ORDER BY age ASC;
-- 按年龄从大到小
SELECT * FROM users ORDER BY age DESC;
-- 只取前 2 条(分页常用)
SELECT * FROM users ORDER BY id LIMIT 2;
-- 跳过前 2 条,再取 2 条(第二页)
SELECT * FROM users ORDER BY id LIMIT 2 OFFSET 2;
-- 去重:查看所有不同的年龄
SELECT DISTINCT age FROM users;

分页公式: LIMIT 每页数量 OFFSET (页码-1) * 每页数量。第 2 页 = LIMIT 2 OFFSET 2,第 3 页 = LIMIT 2 OFFSET 4。


八、聚合函数:统计数据的利器#

-- 一共有多少个用户
SELECT COUNT(*) FROM users;
-- 平均年龄
SELECT AVG(age) FROM users;
-- 最大/最小年龄
SELECT MAX(age), MIN(age) FROM users;
-- 年龄总和
SELECT SUM(age) FROM users;

GROUP BY:按组统计

-- 按年龄分组,看每个年龄有多少人
SELECT age, COUNT(*) FROM users GROUP BY age;

九、约束:给数据”定规矩”#

约束是数据库自动执行的”检查规则”,防止脏数据入库:

约束作用例子
NOT NULL不能为空name VARCHAR(50) NOT NULL
UNIQUE不能重复email VARCHAR(100) UNIQUE
CHECK必须满足条件age INT CHECK (age >= 0)
PRIMARY KEY主键(唯一+非空)id SERIAL PRIMARY KEY
FOREIGN KEY外键,关联别的表user_id INT REFERENCES users(id)

外键(FOREIGN KEY)是数据库最重要的概念之一。它让两张表”关联”起来。


十、多表关联:JOIN(重点!)#

真实项目从来不止一张表。比如:用户表 + 订单表。

-- 订单表:每个订单属于哪个用户
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id), -- 外键:指向 users 表的 id
product VARCHAR(100),
amount NUMERIC(10, 2)
);
INSERT INTO orders (user_id, product, amount) VALUES
(1, '键盘', 199.00),
(1, '鼠标', 89.00),
(3, '显示器', 1299.00);

现在问:每个订单是谁买的? 需要把 orders 和 users 两张表”连起来”查——这就是 JOIN:

SELECT orders.id, users.name, orders.product, orders.amount
FROM orders
JOIN users ON orders.user_id = users.id;

输出:

id | name | product | amount
----+------+---------+--------
1 | 小明 | 键盘 | 199.00
2 | 小明 | 鼠标 | 89.00
3 | 小刚 | 显示器 | 1299.00

JOIN 的原理(用比喻讲): 想象两张表是两本册子,JOIN ON 就是”用订书机把 user_id 等于 users.id 的页订在一起”。ON 后面写”两张表靠什么对上”。

简化写法(表别名):

SELECT o.id, u.name, o.product, o.amount
FROM orders o
JOIN users u ON o.user_id = u.id;

JOIN 的三种常用类型:

-- INNER JOIN:只取两边都有的(默认 JOIN 就是它)
SELECT ... FROM orders JOIN users ON ...;
-- LEFT JOIN:左表全保留,右表没有的显示 NULL
SELECT ... FROM users LEFT JOIN orders ON ...;
-- RIGHT JOIN:右表全保留
SELECT ... FROM users RIGHT JOIN orders ON ...;

实战场景:查每个用户买了多少钱

SELECT u.name, SUM(o.amount) AS total_spent
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.name
ORDER BY total_spent DESC NULLS LAST;

LEFT JOIN 很重要: 用 LEFT JOIN 才能把”没买过东西的用户”也显示出来(金额是 NULL)。


十一、索引:给查询提速#

数据一多(几百万行),全表扫描就慢了。索引就像书的目录——不用翻完整本书,直接定位。

-- 给 email 列建索引(以后按 email 查询会很快)
CREATE INDEX idx_users_email ON users(email);

什么时候建索引:

  • 经常在 WHERE 里用的列(如 email、user_id)
  • 经常 JOIN 的列(外键)

注意: 索引不是越多越好,它会让写入(INSERT/UPDATE)变慢,占用空间。小表完全不用建。

验证查询是否走了索引:

EXPLAIN SELECT * FROM users WHERE email = 'xiaoming@example.com';

看到 Index Scan 说明用上了索引。


十二、事务:要么全成功,要么全失败#

事务(Transaction)保证一组操作”要么全部成功,要么全部回滚”。最经典的例子是转账:

-- 小明转账 100 元给小红
BEGIN; -- 1. 开始事务
UPDATE accounts SET balance = balance - 100 WHERE name = '小明';
UPDATE accounts SET balance = balance + 100 WHERE name = '小红';
-- 2. 执行操作
COMMIT; -- 3. 提交:确认生效

如果中间任何一步出错,可以用 ROLLBACK 撤销:

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE name = '小明';
-- 这里出错了!
ROLLBACK; -- 撤销所有操作,钱没少

事务的四大特性(ACID)记住四个词就行:

  • 原子性:要么全做,要么全不做
  • 一致性:数据始终合理(不会出现钱凭空消失)
  • 隔离性:事务之间互不干扰
  • 持久性:提交后数据不会丢

十三、备份与恢复:数据是命根子#

Terminal window
# 备份整个数据库到文件
pg_dump -U postgres myapp > myapp_backup.sql
# 恢复
psql -U postgres -d myapp < myapp_backup.sql

建议: 定时备份(比如每天一次),这是运维的底线。丢失数据是比丢代码严重得多的事故。


十四、实战:Go 连接 PostgreSQL(和上一篇衔接)#

现在把 Go 和 PG 连起来,做一个真正读写数据库的后端接口。

1. 安装驱动#

Terminal window
go mod init myapp
go get github.com/lib/pq

2. 写代码:查询用户列表#

package main
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
_ "github.com/lib/pq"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
// 连接数据库
// 格式:postgres://用户名:密码@主机:端口/数据库名
connStr := "postgres://postgres:你的密码@localhost:5432/myapp?sslmode=disable"
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal("连接失败:", err)
}
defer db.Close()
// 测试连接
if err := db.Ping(); err != nil {
log.Fatal("数据库连不上:", err)
}
fmt.Println("数据库连接成功!")
// 接口:返回所有用户
http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
// 查询数据库
rows, err := db.Query("SELECT id, name, age FROM users")
if err != nil {
http.Error(w, err.Error(), 500)
return
}
defer rows.Close()
// 遍历结果
var users []User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Name, &u.Age); err != nil {
http.Error(w, err.Error(), 500)
return
}
users = append(users, u)
}
// 返回 JSON
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
})
fmt.Println("服务器启动:http://localhost:8080")
http.ListenAndServe(":8080", nil)
}

运行:

Terminal window
go run main.go

浏览器打开 http://localhost:8080/api/users,就能看到数据库里的用户数据变成了 JSON:

[{"id":1,"name":"小明","age":18},{"id":3,"name":"小刚","age":20}]

这一刻,你已经打通了完整的全栈链路:

前端 (JS/fetch) → 后端 (Go HTTP接口) → 数据库 (PostgreSQL)

用户改数据 → 存进数据库 → 刷新页面数据还在。这才是真实网站的工作方式。


十五、总结#

这一篇我们从零学会了 PostgreSQL:

  • 数据库基础:仓库 → 货架(表)→ 商品(行)
  • 安装与连接:psql 命令行
  • 建表:CREATE TABLE,主键、类型
  • CRUD:INSERT / SELECT / UPDATE / DELETE
  • 排序分页去重:ORDER BY、LIMIT、OFFSET、DISTINCT
  • 聚合函数:COUNT、AVG、MAX、GROUP BY
  • 约束:NOT NULL、UNIQUE、CHECK、外键
  • JOIN 多表查询:把两张表关联起来(重点)
  • 索引:查询提速
  • 事务:ACID,转账例子
  • 备份恢复:pg_dump
  • Go + PG 实战:写一个真正的全栈接口

你的后端学习主线已经完成:

Go 基础 → PostgreSQL → Go 连接数据库 → 全栈打通

下一步就是自己动手建个表、写个增删改查接口,把数据跑起来。数据库是后端的地基,多写多练,很快就熟了。

PostgreSQL(PGSQL)从零开始完整教程(小白也能看懂)
https://021028.xyz/posts/default/75/
作者
021028
发布于
2026-08-11
许可协议
CC BY-NC-SA 4.0