SQL

Posted on By ᵇᵒ

1. 什么是 SQL

SQL 全称 Structured Query Language,用于操作关系型数据库中的数据和数据库对象。
常见的关系型数据库包括:

  • MySQL
  • PostgreSQL
  • SQLite
  • Oracle
  • SQL Server

这些数据库都受到 SQL 标准影响,但并不是所有语法都完全通用。

关系型数据库通常由一组相互关联的表组成。每张表有:

  • 列:描述字段,例如 id、name、created_at
  • 行:一条具体记录

可以把它理解为“比 Excel 更适合存储、约束和查询数据的表格系统”。

2. 常见列类型(column type)

在 SQL/关系型数据库里,每一列(column)都有一个数据类型(type),用于限制该列可以存储什么样的数据。
选择合适的数据类型,会影响:

  • 数据正确性
  • 存储空间
  • 查询性能
  • 可维护性

2.1 数值类型(Numeric Types)

类型 描述 示例
INT / INTEGER 整数 42
BIGINT 大整数 1234567890123
SMALLINT 小整数 1000
DECIMAL(p,s) / NUMERIC(p,s) 精确小数(固定精度) DECIMAL(10,2) → 最多 10 位,总有 2 位小数
FLOAT / REAL / DOUBLE 浮点数(近似) 3.14159

说明:

  • 金额、价格、比例这类对精度敏感的数据,优先用 DECIMAL / NUMERIC
  • 浮点数适合科学计算,不适合直接存钱

2.2 字符类型(String / Text Types)

类型 描述 示例
CHAR(n) 固定长度字符 ‘abc ‘(长度 3)
VARCHAR(n) 可变长度字符 ‘hello’
TEXT / CLOB 大文本 ‘Lorem ipsum…’

说明:

  • CHAR(n) 常用于长度固定的数据,例如国家码、状态码
  • VARCHAR(n) 更常用,适合大多数字符串字段
  • 字符串字面量通常用单引号,例如 ‘Alice’
  • 字符串中若包含单引号,通常写成两个单引号,例如 ‘I’‘m Bob’

2.3 日期时间类型(Date/Time Types)

类型 描述 示例
DATE 年-月-日 2026-04-07
TIME 时:分:秒 14:30:00
DATETIME / TIMESTAMP 日期+时间 2026-04-07 14:30:00
YEAR 年份 2026

说明:

  • TIMESTAMP 和 DATETIME 在不同数据库中的行为并不完全一样
  • PostgreSQL 常用 TIMESTAMP
  • MySQL 同时有 DATETIME 和 TIMESTAMP
  • 如果业务涉及时区,设计时要提前考虑“是否存时区”或“是否统一用 UTC”

2.4 布尔类型(Boolean Type)

类型 描述 示例
BOOLEAN / BOOL 真/假 TRUE / FALSE

说明:

  • BOOLEAN 是常见写法,但不同数据库实现不同
  • MySQL 中 BOOLEAN 本质上是 TINYINT(1) 的别名
  • SQL Server 没有标准 BOOLEAN,通常使用 BIT

2.5 二进制类型(Binary / Blob)

类型 描述 示例
BINARY(n) 固定长度二进制 0x01FF
VARBINARY(n) 可变长度二进制 0xA1B2C3
BLOB 大二进制对象 图片、文件

2.6 JSON / 枚举类型(部分数据库特有)

类型 描述 示例
JSON JSON 数据 ’{“name”:”Alice”}’
ENUM 枚举 ‘small’,’medium’,’large’

2.7 选类型的建议

  • 金额、积分、汇率:优先 DECIMAL / NUMERIC
  • 用户名、标题、手机号:通常用 VARCHAR
  • 超长正文:考虑 TEXT
  • 时间字段:先明确是否要处理时区
  • 想要更强可移植性时,少依赖 ENUM、YEAR 这类方言类型

3. 主键与外键

在关系型数据库中,主键(Primary Key)和外键(Foreign Key)是最核心的概念之一,用于保证数据的唯一性和表之间的关联。

3.1 主键(Primary Key)

主键用来唯一标识表中的每一行记录。一个表只能有 一个主键,但主键可以由多列组成(复合主键)。

  • 唯一性 每行数据的主键值都不相同
  • 非空 主键列不能为 NULL
  • 自动索引 数据库会自动为主键建立索引,提高查询效率

示例:

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100)
);

复合主键示例:

CREATE TABLE orders (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id)
);

3.2 外键(Foreign Key)

外键用于建立 表与表之间的关联。外键列引用另一个表的主键或唯一键,从而保证数据一致性。

  • 参照性完整性 外键值必须在被引用表的主键中存在,或者为 NULL
  • 可为空 外键列可以允许 NULL(表示没有关联)
  • 可级联操作 可以设置 ON DELETE CASCADE 或 ON UPDATE CASCADE 自动更新/删除关联数据

示例:

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    user_id INT,
    order_date DATE,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

这里 user_id 是外键,引用 users 表的 id 列,保证订单一定属于某个用户。

带级联的外键示例:

FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
ON UPDATE CASCADE

含义:

  • 删除用户时,相关订单也会被删掉
  • 更新用户主键值时,关联记录同步更新

级联操作很方便,但要谨慎,避免误删大量数据。

4. 索引

在数据库中,索引(Index)是提高查询效率的重要手段,相当于书的目录,让数据库可以快速找到需要的数据。大多数数据库索引基于 B+ 树实现。

4.1 索引有什么用

  • 加快 WHERE 条件过滤
  • 加快 JOIN 关联
  • 在某些场景下优化 ORDER BY、GROUP BY
  • 对唯一约束提供支持

4.2 索引的代价

索引不是越多越好。索引会带来:

  • 更多存储开销
  • 更慢的 INSERT
  • 更慢的 UPDATE
  • 更慢的 DELETE

因为数据变了,索引也要一起维护。

4.3 常见索引类型

  1. 主键索引(Primary Key Index)
      CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));
    

    自动为主键列建立索引,唯一且非空。

  2. 唯一索引(Unique Index)
      CREATE UNIQUE INDEX idx_email ON users(email);
    

    保证列值唯一,可在非主键列上创建。

  3. 普通索引(Index / Non-Unique Index)
      CREATE INDEX idx_name ON users(name);
    

    仅用于加快查询,允许重复值。

  4. 组合索引(Composite Index)
      CREATE INDEX idx_name_email ON users(name, email);
    

    索引覆盖多列,顺序重要,查询时最好按索引顺序。

4.4 全文索引和空间索引

  1. 全文索引(Fulltext Index)
      CREATE FULLTEXT INDEX idx_content ON articles(content);
    

    用于文本搜索,支持 MATCH … AGAINST 搜索文本。

  2. 空间索引(Spatial Index)
      CREATE SPATIAL INDEX idx_location ON places(location);
    

    用于 GIS / 坐标数据,支持 POINT, GEOMETRY 类型。

全文索引、空间索引都很常见,但它们不是通用入门 SQL 的核心部分,而且不同数据库支持方式差异很大。例如:

  • MySQL 有 FULLTEXT INDEX
  • PostgreSQL 更常见的是全文检索能力配合 tsvector
  • GIS 相关索引在不同数据库中也有不同实现

5. SQL 语句的常见分类

5.1 DDL

Data Definition Language,数据定义语言,定义和修改数据库结构(表、索引、视图等)。常见语句:

  • CREATE 创建数据库、表、视图、索引等
  • ALTER 修改已有数据库对象结构
  • DROP 删除数据库对象
  • TRUNCATE 清空表数据(保留表结构)

5.2 DML

Data Manipulation Language,数据操作语言,用来增删改查数据。常见语句:

  • SELECT 查询数据
  • INSERT 插入数据
  • UPDATE 更新数据
  • DELETE 删除数据

Note

  • UPDATE 和 DELETE 执行之前最好先使用 SELECT 确认约束的数据集合是否是自己的预期。
  • 有的资料把 SELECT 单独分类成 DQL。

5.3 DCL

Data Control Language,数据控制语言,控制数据库访问权限和安全。常见语句:

  • GRANT 授权用户权限
  • REVOKE 收回用户权限

5.5 TCL

Transaction Control Language,事务控制语言,管理事务,确保数据一致性。常见语句:

  • COMMIT 提交事务,永久保存更改
  • ROLLBACK 回滚事务,撤销未提交更改
  • SAVEPOINT 设置事务保存点,可回滚到此点
  • SET TRANSACTION 设置事务属性

6. 常用查询语法

6.1 查(SELECT)

SELECT name, age FROM users;

取哪些字段。
FROM 查询的数据来源,可以是表,也可以是临时结果集。

6.2 筛(WHERE)

SELECT * FROM users WHERE age > 18;

过滤条件

6.3 排(ORDER BY)

ORDER BY age DESC;

排序

6.4 聚(GROUP BY)

SELECT city, COUNT(*) FROM users GROUP BY city;

分组聚合,GROUP BY 后,只能 SELECT 分组字段 or 聚合函数

6.5 连(JOIN)

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

连接多表

6.6 套(子查询)

SELECT name FROM users
WHERE id IN (SELECT user_id FROM orders);

7. 建表、改表(加列、删列、重命名表)、删表

7.1 建表 CREATE TABLE

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE,
    created_at TIMESTAMP
);

7.2 增加列 ALTER TABLE

ALTER TABLE users
ADD COLUMN phone VARCHAR(20);

7.3 删除列

ALTER TABLE users
DROP COLUMN phone;

7.4 重命名表

ALTER TABLE users
RENAME TO app_users;

说明:

  • 不同数据库在 ALTER TABLE 的细节上可能略有不同
  • 写迁移脚本前要确认当前数据库的语法

7.5 删表 DROP TABLE

DROP TABLE app_users;

危险操作建议:

  • 先确认环境是不是生产库
  • 先确认是否有备份

8. 分表、分库

分库分表不是 SQL 基础语法,而是数据库架构层面的设计问题。

8.1 垂直拆分

按业务拆,例如:

  • 用户表放到用户库
  • 订单表放到订单库

优点:

  • 业务边界清晰
  • 单库压力降低

代价:

  • 跨库查询和事务更复杂

8.2 水平拆分

按数据行拆,例如按 user_id 取模,把同一张大表拆到多个表或多个库中。

优点:

  • 单表数据量下降
  • 可以分摊存储和查询压力

代价:

  • 路由复杂
  • 聚合查询复杂
  • 扩容和迁移复杂

9. SQL 执行顺序

SELECT DISTINCT column, AGG_FUNC(column_or_expression), 
FROM mytable
    JOIN another_table
      ON mytable.column = another_table.column
    WHERE constraint_expression
    GROUP BY column
    HAVING constraint_expression
    ORDER BY column ASC/DESC
    LIMIT count OFFSET COUNT;

常见的逻辑执行顺序可以理解为:

  1. FROM
  2. JOIN
  3. WHERE
  4. GROUP BY
  5. HAVING
  6. SELECT
  7. DISTINCT
  8. ORDER BY
  9. LIMIT / OFFSET

这也是为什么:

  • WHERE 里不能直接使用 SELECT 阶段才产生的别名
  • HAVING 是对分组后的结果再做筛选

10. 学习资源

基础

进阶

刷题