← 返回知识宇宙
数据库·进阶

SQL 入门:从一张表看懂数据库

本文从一张表入手,系统讲解 SQL 数据库核心概念与操作,涵盖建库查数据、列记录主键、约束索引、DDL/DML/DQL/DCL/TCL 五类 SQL 语言、SELECT 聚合分组排序、JOIN 多表拼接、五大范式与反范式、事务与 ACID、视图索引权限、SQLite/PostgreSQL/MySQL 对比、Python 连接数据库、ORM 映射以及 DBeaver 图形化管理,适合快速建立 SQL 全景。

知识编号36
关系轨道4
学习难度进阶
SQL数据库入门教程关系型数据库SQLitePostgreSQLMySQLPythonORMDBeaver
抖音

视频讲解

SQL 入门:从一张表看懂数据库

一个视频,讲透所有 SQL 数据库,不用学几个月。


第一章 - 数据库到底是什么

这一个视频把 SQL 的核心一次讲完:建库、查数据、连 Python、用 DBeaver。

一句话:SQL 存的是{{结构化表数据}}——一张张表,表里是一行行记录、一列列字段,每列有固定类型。

一个类比:数据库是仓库,表是货架

[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 是你取货、放货、盘点时说的那句话。


第二章 - 一张表的核心零件与约束

列、记录、主键

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

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 四个字母

隔离级别:并发时的四种取舍

级别防止什么代价
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 后端最常见的选择。生态最大,教程最多,中小型网站几乎默认用它。

一张表对比

维度SQLitePostgreSQLMySQL
形态单文件嵌入式独立服务器独立服务器
默认端口无54323306
强项零配置、原型严谨、扩展、JSONBWeb 生态、复制
适合本地/单机复杂业务网站后端

本章小结

三者都说同一套 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 关系图

它能帮你做什么

本章小结

DBeaver 是数据库的图形界面,适合开发期探索;但该懂的命令和备份习惯,一个都不能少。


总结

实践示例

引用此知识点

更多数据库知识