SQL 入门:从一张表看懂数据库
本文从一张表入手,系统讲解 SQL 数据库核心概念与操作,涵盖建库查数据、列记录主键、约束索引、DDL/DML/DQL/DCL/TCL 五类 SQL 语言、SELECT 聚合分组排序、JOIN 多表拼接、五大范式与反范式、事务与 ACID、视图索引权限、SQLite/PostgreSQL/MySQL 对比、Python 连接数据库、ORM 映射以及 DBeaver 图形化管理,适合快速建立 SQL 全景。
视频讲解
SQL 入门:从一张表看懂数据库
一个视频,讲透所有 SQL 数据库,不用学几个月。
第一章 - 数据库到底是什么
这一个视频把 SQL 的核心一次讲完:建库、查数据、连 Python、用 DBeaver。
一句话:SQL 存的是{{结构化表数据}}——一张张表,表里是一行行记录、一列列字段,每列有固定类型。
一个类比:数据库是仓库,表是货架
- 数据库(database)= 整个仓库
- 数据表(table)= 仓库里的一排货架
- 记录(record)= 货架上的一行货
- 列(column)= 货架上贴的标签
[icon: database] 数据库 = 仓库
└─ [icon: table] users 表 = 一排货架
├─ 列: id / name / age(标签)
└─ 记录: 1 | Ada | 28(一行货)
亲手跑一次
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
);
INSERT INTO users VALUES (1, 'Ada', 28), (2, 'Lin', 30);
SELECT * FROM users WHERE age > 28;
id | name | age
---|------|----
2 | Lin | 30
三句话:建表、塞数据、按条件查。这就是 SQL 最核心的循环。
本章小结
数据库是仓库,表是货架,记录是货,列是标签——SQL 是你取货、放货、盘点时说的那句话。
第二章 - 一张表的核心零件与约束
列、记录、主键
- {{列 column}}:字段,带类型(INT / TEXT / DECIMAL),决定怎么存、怎么比。
- {{记录 record}}:一行数据。
- {{主键 primary key}}:一行的唯一身份证,绝不重复。
users 表
id(主键) | name | age
1 | Ada | 28 <- 记录
2 | Lin | 30
六种约束:让数据不敢乱来
约束是数据库的"规矩",写在建表时,坏数据根本进不来:
CREATE TABLE users (
id INTEGER PRIMARY KEY, -- 主键:唯一 + 非空
name TEXT NOT NULL, -- NOT NULL:不能为空
email TEXT UNIQUE, -- UNIQUE:不能重复
age INTEGER CHECK (age >= 0), -- CHECK:范围校验
role TEXT DEFAULT 'user' -- DEFAULT:默认值
);
外键是第六种约束(下面专门讲)。约束把{{校验}}交给数据库,程序再手滑也写不坏。
外键:把两张表连起来
订单表里存"谁下的单",只存用户主键,不抄姓名。这个指向另一张表主键的列,叫{{外键 foreign key}}。
users: id(主键) | name
orders: id(主键) | user_id(外键) | amount
orders.user_id -> users.id
索引:给查找装个"目录"
索引像书的{{目录}}:没有它,数据库从头翻到尾;有了它,直接跳到那一页。常见有 B+ 树(范围查询)和 Hash(等值查询)。
CREATE INDEX idx_users_name ON users(name);
本章小结
主键认行,外键连表,约束护数据,索引加速——一张表能不能长期不烂,就靠这几样。
第三章 - SQL 语言全景:DDL / DML / DQL / DCL / TCL
五类语言各管一件事
SQL 按作用分成五类,不用背,看懂首字母就行:
SQL 五大类
├─ DDL 定义: CREATE / ALTER / DROP(管结构)
├─ DML 操作: INSERT / UPDATE / DELETE(管数据)
├─ DQL 查询: SELECT(管查询)
├─ DCL 控制: GRANT / REVOKE(管权限)
└─ TCL 事务: COMMIT / ROLLBACK(管事务)
DDL 与 DML:定义结构和操作数据
-- DDL:表长什么样
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);
ALTER TABLE users ADD COLUMN email TEXT;
DROP TABLE users;
-- DML:货怎么动
INSERT INTO users (id, name) VALUES (1, 'Ada'); -- 增
UPDATE users SET name = 'Ada L.' WHERE id = 1; -- 改
DELETE FROM users WHERE id = 1; -- 删
最常见的致命错误
UPDATE 或 DELETE 忘写 WHERE,会把{{整张表}}改掉或清空。
DELETE FROM users; -- 危险:删光整表
DELETE FROM users WHERE id=1; -- 安全:只删一行
先写 WHERE,再写动作,是保命习惯。
本章小结
DDL 建结构,DML 动数据,DQL 查数据,DCL 管权限,TCL 管事务——五类语言记住了,SQL 就通了一大半。
第四章 - SELECT 进阶:聚合、分组、排序、子查询
五个聚合函数
COUNT 数行、SUM 求和、AVG 平均、MAX 最大、MIN 最小——把一堆行压成一个数。
SELECT COUNT(*), SUM(amount), AVG(amount), MAX(amount), MIN(amount)
FROM orders;
分组、过滤、排序、限量
-- 每个用户下了多少单、总金额,只留大于 50 的,按金额降序取前 10
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 50
ORDER BY total DESC
LIMIT 10;
关键顺序:{{WHERE 筛行 -> GROUP BY 分组 -> HAVING 筛组 -> ORDER BY 排序 -> LIMIT 限量}}。
子查询与 UNION
-- 子查询:查"下单金额超过平均值的订单"
SELECT * FROM orders
WHERE amount > (SELECT AVG(amount) FROM orders);
-- UNION:合并两个结果集(列数一致,自动去重)
SELECT name FROM users
UNION
SELECT name FROM admins;
本章小结
SELECT 的完整套路:聚合压数、GROUP BY 分组、HAVING 筛组、ORDER BY 排序、LIMIT 限量、子查询套娃、UNION 合并。
第五章 - JOIN:把多张表拼起来
为什么需要 JOIN
外键把用户和订单连起来,但要同时看到"谁、买了什么、多少钱",就得把两张表{{拼}}到一起。
users orders
id | name id | user_id | amount
1 | Ada 1 | 1 | 19.99
2 | Lin 2 | 2 | 35.50
3 | Bob (Bob 没有订单)
四种最常用的 JOIN
- {{INNER JOIN 内连接}}:只留两边都匹配的行(Bob 被丢掉)。
- {{LEFT JOIN 左连接}}:左表全保留,右边没有填 NULL(Bob 也出现)。
- {{RIGHT JOIN 右连接}}:右表全保留,左边没有填 NULL。
- {{FULL OUTER JOIN 全外连接}}:两边全保留,缺的填 NULL。
SELECT users.name, orders.amount
FROM users INNER JOIN orders ON users.id = orders.user_id;
SELECT users.name, orders.amount
FROM users LEFT JOIN orders ON users.id = orders.user_id;
INNER 结果: LEFT 结果:
Ada | 19.99 Ada | 19.99
Lin | 35.50 Lin | 35.50
Bob | NULL
还有 {{CROSS JOIN}}(笛卡尔积,两边每行两两配对)和 {{自连接}}(表连自己,如员工表查"经理是谁")。
常见错误
JOIN 忘了 ON 条件,行数 = 左表 × 右表,直接爆炸;或 LEFT/RIGHT 方向搞反,丢掉该保留的行。
本章小结
JOIN 是拼表:INNER 取交集,LEFT 保左,RIGHT 保右,FULL 全要——想保谁,就写哪个方向。
第六章 - 五大范式与现实的妥协
一张什么都塞的外卖订单表
做外卖 App 时,新手最容易把订单、用户、菜品、商家、骑手全塞进一张表:
坏表: 外卖订单(订单号, 用户, 用户电话, 送餐地址, 菜品, 数量, 菜品价格, 商家, 骑手, 骑手电话)
数据一多,用户电话改一处漏一处,菜品涨价要改几十行。范式是一套{{拆表规则}},把这张大表拆成不冗余、不矛盾的一组小表。
1NF 原子性:一格只放一个值
一单点两个菜,"菜品"列塞了"宫保鸡丁、可乐",一格两个值,违反第一范式。
违反 1NF: 菜品 = "宫保鸡丁,可乐"(一格塞两个值)
修复: 拆出订单明细表,每行只放一个菜
2NF 消除部分依赖:价格只跟菜品走
订单明细表主键是(订单号, 菜品)。但"菜品价格"只依赖"菜品",不依赖整个主键,这叫{{部分依赖}},违反第二范式。
订单明细(订单号, 菜品, 数量, 菜品价格)
主键 = (订单号, 菜品)
菜品价格 -> 只依赖 菜品 -> 拆出 菜品表(菜品, 价格)
3NF 消除传递依赖:电话跟着用户走
订单表里"用户电话"依赖"用户",而"用户"依赖"订单号"——电话通过用户间接依赖主键,这叫{{传递依赖}},违反第三范式。
订单(订单号, 用户, 用户电话, 送餐地址)
用户电话 -> 依赖 用户 -> 依赖 订单号 -> 拆出 用户表(用户, 电话, 地址)
BCNF:主属性之间也不能乱依赖
前三范式只管"非主属性"。BCNF 更严:主属性之间的依赖也要消除。送餐地址表里"邮编"决定"城市",而邮编不是候选键,违反 BCNF。
送餐地址(街道, 城市, 邮编)
邮编 -> 城市(一个邮编对应一个城市)
邮编 不是候选键,却决定了主属性 城市 -> 拆表
4NF 消除多值依赖:车型和区域要分开
一个骑手会骑多种车型,又负责多个区域,两件事互相独立。塞进一张表会两两相乘产生冗余,这叫{{多值依赖}},违反第四范式。
骑手(骑手, 车型, 负责区域)
骑手1: 车型=[电动车, 摩托车] 区域=[海淀, 朝阳]
2 x 2 = 4 行,实为两个独立关系 -> 拆成 骑手车型 + 骑手区域
但现实不可能完全按五大范式
拆到 1、2、3NF,绝大多数业务已经够用,团队通常停在{{3NF}}这个甜点区。到 BCNF、4NF,表越拆越碎,一次查询要 JOIN 一堆表。于是工程上做{{反范式}}(denormalization):故意把字段冗余回去,少 JOIN,查询更快。
范式:消除冗余 -> 数据一致 -> 但查询要 JOIN 很多表
反范式:适当冗余 -> 少 JOIN -> 查询快 -> 但要小心同步
本章小结
五大范式是递进的拆表规则:1NF 拆值、2NF 拆部分依赖、3NF 拆传递依赖、BCNF 拆主属性依赖、4NF 拆多值依赖——现实停在 3NF,再按性能反范式。
第七章 - 事务与 ACID
事务:要么全成,要么全不成
转账要扣 A 的钱、加 B 的钱,两步必须一起成功。如果扣了 A 的钱、加 B 时崩了,钱就凭空消失。{{事务}}把一组操作捆成"不可分割"的一包。
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 扣 A
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 加 B
COMMIT; -- 全部成功才提交;出错就 ROLLBACK 回滚
ACID 四个字母
- {{Atomicity 原子性}}:一组操作,要么全做,要么全不做。
- {{Consistency 一致性}}:事务前后,数据都满足约束规则。
- {{Isolation 隔离性}}:并发事务互不干扰,像串行执行。
- {{Durability 持久性}}:提交后,宕机也不丢。
隔离级别:并发时的四种取舍
| 级别 | 防止什么 | 代价 |
|---|---|---|
| READ UNCOMMITTED | 几乎没有 | 快但脏读 |
| READ COMMITTED | 脏读 | PostgreSQL 默认 |
| REPEATABLE READ | 脏读、不可重复读 | MySQL 默认 |
| SERIALIZABLE | 全部异常 | 最慢 |
越严格越安全,但越慢。这就是为什么同样的 SQL,在不同数据库、不同隔离级别下结果可能不一样。
本章小结
事务是"打包执行",ACID 是它的四个承诺,隔离级别是安全与速度之间的旋钮。
第八章 - 视图、索引与权限
视图:存好的查询
重复写一大段 JOIN 太累,把它存成{{视图}},以后当表用。
CREATE VIEW user_orders AS
SELECT users.name, orders.amount
FROM users JOIN orders ON users.id = orders.user_id;
SELECT * FROM user_orders; -- 直接查视图
视图是"虚拟表",本身不存数据,查的是背后那张真实表的实时结果。
索引:加速也有代价
索引加速查询,但每次写数据都要维护索引,所以不是越多越好。B+ 树适合范围查询和排序,Hash 适合等值查找。
CREATE INDEX idx_orders_user ON orders(user_id);
权限:谁只能看,谁能改
DCL 用 GRANT 授权、REVOKE 收权,把最小权限给每个角色。
GRANT SELECT ON users TO app_user; -- 只能看
REVOKE DELETE ON users FROM app_user; -- 收掉删除权
本章小结
视图封装查询,索引加速查找(有代价),权限控制谁能做什么——这三样是生产库的日常。
第九章 - 三大数据库深入:SQLite / PostgreSQL / MySQL
SQLite:嵌入式的单文件
{{SQLite}} 不是一个服务器,而是一个库,整个数据库就是磁盘上的一个 .db 文件。零配置、零部署,App 内置、本地工具、测试原型最合适。并发写能力弱,不适合高并发网站。
PostgreSQL:功能最全的"瑞士军刀"
{{PostgreSQL}} 以严谨和扩展性著称:{{MVCC}} 高并发读不阻塞写,原生 {{JSONB}} 存半结构化数据,支持数组、全文检索,还能装 {{pgvector}} 做向量检索。复杂业务、要强一致性的场景首选。
MySQL:Web 之王
{{MySQL}} 靠 {{InnoDB}} 引擎提供事务和外键,主从复制成熟,是 Web 后端最常见的选择。生态最大,教程最多,中小型网站几乎默认用它。
一张表对比
| 维度 | SQLite | PostgreSQL | MySQL |
|---|---|---|---|
| 形态 | 单文件嵌入式 | 独立服务器 | 独立服务器 |
| 默认端口 | 无 | 5432 | 3306 |
| 强项 | 零配置、原型 | 严谨、扩展、JSONB | Web 生态、复制 |
| 适合 | 本地/单机 | 复杂业务 | 网站后端 |
本章小结
三者都说同一套 SQL:先学通 SQL 本身,再按场景挑 SQLite(单机)、PostgreSQL(严谨)、MySQL(Web)。
第十章 - 用 Python 连接数据库
三种驱动,三个连接
程序访问数据库,得先通过{{驱动}}建立连接。Python 连三种数据库,套路几乎一样:
# SQLite:内置 sqlite3,零配置
import sqlite3
conn = sqlite3.connect("shop.db")
# PostgreSQL:pip install psycopg2
import psycopg2
conn = psycopg2.connect(
host="localhost", port=5432,
user="postgres", password="secret", dbname="shop"
)
# MySQL:pip install pymysql
import pymysql
conn = pymysql.connect(
host="localhost", port=3306,
user="root", password="secret", database="shop"
)
连接之后的标准五步
cur = conn.cursor() # 1. 拿游标
cur.execute("SELECT name FROM users") # 2. 执行
rows = cur.fetchall() # 3. 取结果
conn.commit() # 4. 提交(写了数据才需要)
conn.close() # 5. 关闭连接
常见错误
写了数据忘 commit(),以为存进去了其实没落盘;用完不 close(),连接越积越多拖垮数据库,这叫{{连接泄漏}}。
本章小结
连接 = 驱动 + 参数;操作 = cursor / execute / fetch / commit / close,三种数据库几乎都是这五步。
第十一章 - ORM:把表变成对象
什么是 ORM
程序里操作的是 User 对象,数据库里存的是 users 表。{{ORM(对象关系映射)}}就是中间的翻译官:
User 对象 users 表
{ id, name } <-> id | name
\ ORM /
用 SQLAlchemy 写一遍
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, Session
Base = declarative_base()
class User(Base): # 类 = 表
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
engine = create_engine("sqlite:///shop.db")
Base.metadata.create_all(engine) # 自动建表
with Session(engine) as session:
session.add(User(name="Ada")) # 新增一行
session.commit()
ada = session.query(User).filter_by(name="Ada").first()
print(ada.id, ada.name)
用对象操作,不手写 SQL,换数据库也不用改代码。
常见错误
以为有了 ORM 就不用懂 SQL——ORM 只是翻译,翻译出的 SQL 可能低效(如 N+1 查询)。懂底层 SQL 才能判断 ORM 好不好。
本章小结
ORM 把表和对象互相映射,写代码更顺手;但它是 SQL 的封装,不是替代品。
第十二章 - DBeaver:图形化管理数据库
什么是 DBeaver
{{DBeaver}} 是免费开源的数据库图形工具,跨平台,能连 MySQL、PostgreSQL、SQLite 等几乎所有数据库。它像数据库的{{文件管理器}}。
DBeaver 界面
├─ 左侧: 数据库连接树(MySQL / PostgreSQL / SQLite)
├─ 中间: 表数据、SQL 编辑器、查询结果
└─ 右侧: 表结构、ER 关系图
它能帮你做什么
- {{可视化建表}}:点几下新建表、加列、设主键外键。
- {{浏览数据}}:双击表直接看数据,改起来像 Excel。
- {{画 ER 图}}:自动生成表关系图。
- {{执行 SQL}}:内置编辑器,写完就执行看结果。
本章小结
DBeaver 是数据库的图形界面,适合开发期探索;但该懂的命令和备份习惯,一个都不能少。
总结
- 核心回顾:数据库是仓库,表是货架;主键认行、外键连表、约束护数据、索引加速;五类语言(DDL/DML/DQL/DCL/TCL);聚合分组排序;JOIN 拼表;范式控冗余;事务保一致;视图封装、权限护安全;SQLite/PostgreSQL/MySQL 各有所长;Python 连库、ORM 映射、DBeaver 图形管理。
- 学习路线:先 SQLite 零配置练 SQL -> 再 PostgreSQL 学事务、MVCC、JSONB -> 最后 MySQL 进 Web 实战。
- 推荐练习:① 用 SQLite 建一个"用户 + 订单 + 商品"小商城,跑聚合、GROUP BY、各种 JOIN;② 用 SQLAlchemy 改成对象写法,对比手写 SQL;③ 用 Docker 起 PostgreSQL/MySQL,用 DBeaver 各画一张 ER 图。
实践示例
用 SQLite 建一个包含用户、订单、商品的小商城,练习聚合、GROUP BY、各种 JOIN
使用 SQLAlchemy 将手写 SQL 改为对象写法,对比 ORM 与原生 SQL
用 Docker 启动 PostgreSQL 和 MySQL,并用 DBeaver 各画一张 ER 图
编写 CREATE TABLE 与 INSERT 语句创建 users 表并插入数据,执行 SELECT 条件查询
使用 BEGIN、COMMIT、ROLLBACK 模拟转账操作,理解事务原子性
后续与前置知识
- 数据库导论:12 分钟看懂整个数据库世界前置
面向零基础学习者的数据库导论教程,12分钟建立数据库世界地图,覆盖为何需要数据库、OLTP vs OLAP、9类数据库系统(关系型、键值、文档、宽列、图、搜索、时序、OLAP、向量)、SQL vs NoSQL、共同框架(Schema、数据类型、CRUD、持久化、索引)、对象存储与数据库的区别,以及程序如何通过 Driver、Connection Pool、ORM 连接数据库。
- Python 基础教程 · 10 分钟上手实操课前置
面向零基础初学者的 Python 实操教程,10 分钟从安装运行开始,讲解变量与数据结构、函数与模块、文件 I/O 与异常处理,最后实战完成 CSV 数据分析小工具。
- Docker 演示教程:一次打包,到处运行相关
面向初学者的 Docker 实操教程,10 分钟演示镜像与容器概念、Dockerfile 构建、Docker Compose 多服务编排、常用排错命令,并通过 Node.js + nginx + PostgreSQL 博客项目实战完成容器化部署。
- 数据结构演示教程:按 8 类结构建立全景图相关
10 分钟数据结构全景教程,按 8 类结构(基础存储、线性 ADT、树、Hash、图、集合维护、概率/高级索引、组合型工程结构)梳理数组、链表、栈、队列、堆、哈希表、布隆过滤器、跳表、LRU 缓存等常见数据结构,强调正确分类与典型应用场景。
引用此知识点
- FastAPI 接口开发 演示教程:从 Hello World 到一个完整 CRUD API后续
本文通过十个演示和最终实战,使用 FastAPI 官方教程风格,从三行代码的 Hello World 开始,逐步讲解路径参数、查询参数、请求体、Pydantic 校验、自动文档、异常处理、枚举类型、PUT/DELETE,并最终实现一个基于内存字典的完整 CRUD API,全程使用 TestClient 测试,无需启动服务器。
- 数据分析双引擎 演示教程:同一份泰坦尼克数据,pandas 与 polars 各写一遍相关
使用泰坦尼克乘客数据集,通过 pandas 演示数据读取、筛选、分组聚合、缺失值处理、合并、时间序列等核心数据分析操作,并用 polars 实现相同任务进行对比,展示两种库的 API 差异与各自的性能特点,帮助学习者根据场景选择合适的工具。
- 文档数据库入门:MongoDB 与 Couchbase相关
本教程系统讲解文档数据库核心概念,以 MongoDB 和 Couchbase 为代表,涵盖数据模型、CRUD 操作、查询与聚合、索引、副本集、分片、事务、多模型能力、选型对比及 Python 接入方法,帮助建立文档数据库全景认知。
- 图数据库入门:Neo4j相关
本文系统讲解图数据库核心概念,以 Neo4j 为代表,涵盖节点、边、路径的数据模型,Cypher 图查询语言,多跳遍历与最短路径,社交网络、知识图谱、风控、推荐等典型场景,对比关系型数据库的 JOIN 查询,并演示使用 Python 驱动连接 Neo4j。
- 键值数据库入门:Redis 与 DynamoDB相关
本教程系统讲解键值数据库核心概念,以 Redis 和 DynamoDB 为代表,对比内存与云托管两种形态,涵盖 Redis 五种数据结构、过期持久化高可用、经典场景,DynamoDB 数据模型、主键设计、CRUD、二级索引、容量一致性、单表设计,并通过 Python 示例连接两者,最后给出选型建议与练习。
- 分析型数据库入门:ClickHouse、DuckDB、Snowflake、BigQuery前置
本教程系统讲解分析型数据库(OLAP)核心概念,以 ClickHouse、DuckDB、Snowflake、BigQuery 为代表,涵盖列式存储、向量化执行、存算分离与无服务器架构,对比四者选型,并演示 Python 连接与适用场景,帮助快速建立分析型数据库全景认知。
- 搜索数据库入门:Elasticsearch 与 OpenSearch相关
本教程系统讲解搜索数据库核心概念,以 Elasticsearch 与 OpenSearch 为代表,涵盖倒排索引、集群与索引/分片/副本数据模型、REST API 全文搜索、分词与 BM25 相关性排序、聚合分析、两者对比及 Python 接入方法,帮助建立搜索数据库全景认知。
- 时序数据库入门:InfluxDB 与 TimescaleDB前置
本文系统讲解时序数据库核心概念,以 InfluxDB 和 TimescaleDB 为代表,涵盖数据模型、Line Protocol 与 Flux/InfluxQL 查询、保留策略与降采样、超表与连续聚合/压缩、两者对比及 Python 接入方法,帮助建立时序数据库全景认知。
- 宽列数据库入门:Cassandra 与 ScyllaDB相关
本教程系统讲解宽列数据库核心概念,以 Cassandra 和 ScyllaDB 为代表,涵盖数据模型(分区键+列族)、CQL 与 CRUD、无主架构与可调一致性、LSM 树与建模、ScyllaDB 的 Shard-per-core 架构、两者对比、Python 连接及适用场景,帮助快速建立宽列数据库全景认知。