CSNotesCSNotes
MySQL
Java
SSM
Redis
学习路线
TODO
实用工具
GitHub
MySQL
Java
SSM
Redis
学习路线
TODO
实用工具
GitHub
  • 第一章 SQL 语法

    • 一、MySQL 语法基础
    • 二、多表查询与笛卡尔积
    • 三、JOIN 类型与算法
    • 四、LIKE 通配符与正则
    • 五、GROUP BY/HAVING /窗口函数
    • 六、字段类型与选型
    • 七、约束、主键与 DDL 语法
    • 八、SQL 实用函数
    • 九、TRUNCATE
    • 十、数据建模范式
    • 十一、exists 子查询
    • 十二、select *
  • 第二章 SQL 执行

    • 一、一条建表语句是如何执行的?
    • 二、EXPLAIN 执行计划
    • 三、一条 DELETE 语句在干什么?
    • 四、EXPLAIN 各列的含义
    • 五、JOIN 执行算法
  • 第三章 存储结构

    • 一、字符集与 CHAR/VARCHAR 存储
    • 二、最大行大小 65535 bytes
    • 三、为什么 VARCHAR 上限是 16383?
    • 四、表、页与 Page Header 概念
    • 五、InnoDB 行格式 COMPACT
    • 六、磁盘空间管理器
  • 第四章 索引

    • 一、为什么是 B+ 树?
    • 二、B+ 树插入、分裂、合并的完整过程
    • 三、容量计算:一颗高度为 3 的 B+ 树能存多少条数据?
    • 四、索引分类(聚簇/二级/联合/覆盖)
    • 五、联合索引 + 最左匹配原则
    • 六、索引失效 7 大场景
    • 七、索引创建策略:三星索引法
    • 八、执行计划
  • 第五章 事务

    • 一、事务
    • 二、事务的本质(内核视角)
    • 三、MVCC 多版本并发控制
    • 四、死锁四条件
    • 五、一条 INSERT 语句背后在干吗?
  • 第六章 日志

    • 一、Redo Log 结构
    • 二、Undo Log
    • 三、预写日志 WAL(Redo/Undo)
    • 四、两阶段提交 2PC
    • 五、崩溃恢复
    • 六、三种刷盘组合性能与一致性 tradeoff
  • 第七章 锁

    • 一、锁

一、MySQL 语法基础

MySQL 使用三值逻辑 —— TRUE, FALSE 和 UNKNOWN。任何与 NULL 值进行的比较都会与第三种值 UNKNOWN 做比较。这个“任何值”包括 NULL 本身!这就是为什么 MySQL 提供 IS NULL 和 IS NOT NULL 两种操作来对 NULL 特殊判断。

-- 先删旧表,方便反复测试
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS users;

-- 用户表
CREATE TABLE users (
                       id   INT PRIMARY KEY,
                       name VARCHAR(50) NOT NULL,
                       age  INT NULL
) ;

-- 订单表
CREATE TABLE orders (
                        id      INT PRIMARY KEY,
                        user_id INT NOT NULL,
                        amount  DECIMAL(10,2) NOT NULL,
                        status  TINYINT NOT NULL DEFAULT 0,
                        KEY idx_user_id (user_id)
) ;

-- 用户数据
INSERT INTO users (id, name, age) VALUES
                                      (1, '张三', 20),
                                      (2, '李四', 25),
                                      (3, '王五', NULL),
                                      (4, '赵六', 30);

-- 订单数据
-- 注意:user_id = 5 的用户在 users 表里不存在
INSERT INTO orders (id, user_id, amount, status) VALUES
                                                     (1, 1, 100, 1),
                                                     (2, 1, 200, 1),
                                                     (3, 2, 300, 0),
                                                     (4, 5, 400, 1);

(一)、查询基础

(二)、连接

inner join

-- 找出所有有有效订单的用户
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
         INNER JOIN orders o
                    ON u.id = o.user_id
and o.status = 1;

-- 找出所有有有效订单的用户
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
         INNER JOIN orders o
                    ON u.id = o.user_id
        where o.status = 1;

上面这两种写法都可以,但是建议 on 里写关联条件,where 里写过滤条件

-- 找出有 status=1 订单的用户
SELECT u.id, u.name
FROM users u
         INNER JOIN orders o ON u.id = o.user_id
WHERE o.status = 1;

-- 找出有 status=1 订单的用户
SELECT u.id, u.name
FROM users u
WHERE EXISTS (SELECT 1
              FROM orders o
              WHERE o.user_id = u.id
                AND o.status = 1);

如果 select 里只有一张表的字段,建议用 exists 语义更清晰

LEFT JOIN 保留左表所有行,右表无匹配时字段填 NULL。

left join 过滤左表的条件写在 on 里和 where 里一样,都是过滤用户

-- 展示 id=1 的那个用户及其所有订单,没订单的显示null
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
         LEFT JOIN orders o
                   ON u.id = o.user_id
and u.id = 1;

-- 同上
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
         LEFT JOIN orders o
                   ON u.id = o.user_id
                       where u.id = 1;


where 过滤右边条件=inner join,找出有有效订单的客户

-- 找出有有效订单的客户(不建议使用)
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
         LEFT JOIN orders o
                   ON u.id = o.user_id
                  WHERE o.status = 1;

-- 找出所有用户+status=1的订单,其他订单显示null
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
         LEFT JOIN orders o
                   ON u.id = o.user_id
AND o.status = 1;

警告

不要写 LEFT JOIN ... WHERE 右表字段 = x —— 这是最常见的语义陷阱,它既不是真正的 LEFT JOIN,又让代码意图模糊。

根本原因: LEFT JOIN 时,未匹配的右表字段会被填成 NULL。任何 右表字段 = 值 或 右表字段 > 值 这类条件,都会把 NULL 行排除掉。

exists

判断 A 表的数据是否在 B 表中存在。

not exists

写法 A:NOT EXISTS

-- 找出没有任何订单的用户
SELECT u.id, u.name
FROM users u
WHERE NOT EXISTS (SELECT 1
                  FROM orders o
                  WHERE o.user_id = u.id);

写法 B:LEFT JOIN + IS NULL(上一轮提过的反连接)

-- 等价写法
SELECT u.id, u.name
FROM users u
         LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;

limit

limit 和 offset 后面只接收正整数

distinct 和 group by 去重有什么区别?

SQL 标准要求 select 里的非聚合字段,必须出现在 group by 里

group by 就是一行行拿出来分组。

having

过滤分组。

窗口函数 vs group by 能否保留原始列

窗口函数也是分组计算,但不会向GROUP BY那样“把一组压缩成一行”;它保留原始每一行,在每行旁边额外算出一个结果。

-- 找出每个用户订单最高金额的那一行
select *
from (select id,
             user_id,
             amount,
             row_number() over (partition by user_id order by amount desc) as rn
      from orders) t
where rn = 1;

-- 每个用户的最高金额这个数值
SELECT user_id, MAX(amount) AS max_amount
FROM orders
GROUP BY user_id;


-- 按用户分组,计算每个用户订单总金额
SELECT user_id, SUM(amount) AS user_total
FROM orders
GROUP BY user_id;

--订单最多的企业
select customer_number from Orders group by customer_number order by count(*) desc limit 1

--订单最多的企业(可并列)
select customer_number from (
    select customer_number ,dense_rank() over (order by count(order_number) desc ) as rk
    from Orders
    group by customer_number
) as a  where a.rk=1
编辑此页
上次更新: 2026/10/22 14:50
Next
二、多表查询与笛卡尔积