
简介这份PDF笔记整理自B站Mosh老师三小时SQL入门课程面向零基础或希望系统梳理SQL语法的开发者与数据库初学者帮助其快速建立查询语言的知识框架。内容覆盖SQL基础概念、SELECT子句、WHERE条件筛选、AND/OR/NOT逻辑操作符以及IN、BETWEEN、LIKE、REGEXP等常用操作符的语法与用法并延伸至IS NULL、ORDER BY、LIMIT、内外连接、USING、交叉连接、UNION与数据插入等主题形成由浅入深的完整脉络。资源包共1个PDF文件大小约2.43MB排版紧凑、便于打印或对照练习适合作为速查手册随用随翻。目前已有981人学习说明其在入门群体中具备一定认可度。读者可借此快速回顾课程要点在写查询时对照语法示例查漏补缺也可作为进一步学习SQL的起点。1. 三小时速通 SQL一份能直接当查询手册用的 Cheat Sheet刚接手一个后台报表需求产品经理甩来一句“帮我查一下上个月下单但没付款的用户”你打开数据库客户端SELECT 会写JOIN 记得个大概可LEFT JOIN和INNER JOIN到底差在哪、LIMIT 6, 3是跳过 6 条还是取第 6 条脑子里开始打鼓。这种时候翻官方文档太慢搜零散博客又怕版本对不上——一份结构紧凑、语法覆盖到 JOIN 和 UNION 的 SQL 速查表就是救急的东西。这份资源是某位讲师三小时入门课程的配套笔记把 SELECT、WHERE、逻辑运算符、IN/BETWEEN/LIKE/REGEXP、ORDER BY、LIMIT、各类 JOIN、UNION、INSERT 串成一条线示例统一跑在sql_store示例库上。适合刚接触关系型数据库、需要在 MySQL 环境里快速把查询写对的人也适合有经验但想找一份可打印对照表的从业者。2. 把速查表跑起来示例库、语句骨架与过滤条件拿到一份 Cheat Sheet最怕的是“看得懂但跑不通”。这份笔记所有示例都默认存在一个sql_store库和customers、orders、colors、sizes、clients几张表不先把这套环境搭出来后面每条语句都只能干看。这一章先把运行环境和语句骨架立住再把 WHERE 这一层的过滤手段拆开讲透。2.1 先建一套能跑的示例库笔记里没有附带建表脚本常见做法是自己按示例字段反推一张最小表结构。下面这段 DDL 覆盖了笔记中出现的全部列字段类型按 MySQL 常规选型给够跑通所有示例-- 建库并切换 CREATE DATABASE IF NOT EXISTS sql_store; USE sql_store; -- 客户表笔记里用到 first_name、state、points、birthdate、phone CREATE TABLE customers ( customer_id INT PRIMARY KEY AUTO_INCREMENT, first_name VARCHAR(50) NOT NULL, state CHAR(2), points INT DEFAULT 0, birthdate DATE, phone VARCHAR(20) ); -- 订单表JOIN 示例依赖 customer_id 关联 CREATE TABLE orders ( order_id INT PRIMARY KEY AUTO_INCREMENT, customer_id INT, order_date DATE, amount DECIMAL(10,2), FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ); -- 交叉连接示例用的两张小表 CREATE TABLE colors (color VARCHAR(20)); CREATE TABLE sizes (size VARCHAR(20)); -- UNION 示例的第二张结果集来源 CREATE TABLE clients ( name VARCHAR(50), address VARCHAR(100) );建完表灌几条测试数据就能开跑customers至少要有几条state不同、points分布在不同区间、phone有 NULL 的记录否则IS NULL和BETWEEN的示例查出来是空集容易误判成语句写错。这里有个容易忽略的点points给了DEFAULT 0后面 INSERT 示例里的DEFAULT关键字才有意义建表时别图省事把默认值省掉。环境就绪后笔记开篇那条综合语句值得先跑一遍它把 SELECT、WHERE、ORDER BY、LIMIT 四层串在一起USE sql_store; SELECT * FROM customers WHERE state CA ORDER BY first_name LIMIT 3;这条语句的执行顺序不是书写顺序先FROM定位表再WHERE过滤然后ORDER BY排序最后LIMIT截断。理解这个顺序很关键——WHERE里不能用SELECT中定义的列别名因为过滤发生在别名生效之前而ORDER BY里可以用别名。笔记里提到 SQL 不区分大小写、MySQL 每条语句必须以分号结尾这两条是语法层面的硬约束尤其是分号在客户端里漏写会一直等你输入下一条新手常以为是卡死。2.2 SELECT 里的表达式与去重SELECT不只是列名列表还能直接做算术。笔记给的例子是算折扣因子-- 表达式列 别名括号优先先乘除后加减 SELECT (points * 10 20) AS discount_factor FROM customers;运算优先级是括号、乘除、加减和普通算术一致。别名用AS省略AS也能跑但列名里有空格或特殊字符时必须用反引号包起来。表达式列的一个实际价值是把口径固化在查询里比如积分换算、金额含税价避免每次在应用层再算一遍导致口径漂移。去重靠DISTINCT-- 只取不重复的州常用于枚举维度值 SELECT DISTINCT state FROM customers;DISTINCT作用于整行选出的列组合SELECT DISTINCT state, first_name去重的是“州名”这个组合不是单独对 state 去重。这一点在统计维度基数时经常翻车以为去重了州实际出来一堆重复州名。2.3 WHERE 的比较、逻辑与集合运算符WHERE是过滤的主战场。比较运算符笔记列了 !其中不等于有两种写法是标准 SQL!是 MySQL 等数据库的扩展跨库迁移时优先用。逻辑运算符AND、OR、NOT组合条件时AND优先级高于OR混用时必须加括号-- 出生日期在 1990 之后 且 积分大于 1000 SELECT * FROM customers WHERE birthdate 1990-01-01 AND points 1000; -- 任一条件成立即可注意 OR 两侧要各自完整 SELECT * FROM customers WHERE birthdate 1990-01-01 OR points 1000; -- NOT 取反括号不能省否则只作用于紧邻条件 SELECT * FROM customers WHERE NOT (birthdate 1990-01-01);IN用来替代一长串OR可读性和执行计划通常都更好-- 命中 VA、NY、CA 三个州的客户 SELECT * FROM customers WHERE state IN (VA, NY, CA);BETWEEN是闭区间两端都包含-- 积分在 100 到 200 之间含 100 和 200 SELECT * FROM customers WHERE points BETWEEN 100 AND 200;这里有个血泪经验BETWEEN用在日期上时如果列是DATETIME类型BETWEEN 2024-01-01 AND 2024-01-31会漏掉 1 月 31 日当天有时分秒的记录因为右端被当成00:00:00。日期范围查询更稳的写法是 起始日 AND 次日。LIKE做通配匹配%匹配任意长度字符_匹配恰好一个字符-- 名字以 b 开头 SELECT * FROM customers WHERE first_name LIKE b%;%放在开头LIKE %b会导致索引失效大表上要谨慎。REGEXP则把正则能力带进 WHERE笔记给的几个模式很实用-- 以 a 开头 WHERE first_name REGEXP ^a -- 以 ey 或 on 结尾 WHERE first_name REGEXP ey$|on$ -- 以 my 开头 或 包含 se WHERE first_name REGEXP ^my|se -- 包含 b且 b 后面跟 r 或 u WHERE first_name REGEXP b[ru]^锚定开头、$锚定结尾、|是逻辑或、[abc]匹配集合内任一单字符、[a-d]匹配区间。注意REGEXP的“包含”语义不加锚点时模式出现在任意位置都算命中这和LIKE %x%类似性能上同样不友好能落到前缀匹配就别用正则。IS NULL单独拎出来说因为 NULL 的比较是个黑匣子-- 没有电话号的客户 SELECT * FROM customers WHERE phone IS NULL;phone NULL永远不成立NULL 参与的任何比较结果都是 UNKNOWN必须用IS NULL/IS NOT NULL。同理WHERE phone 1234会把 phone 为 NULL 的行排除掉因为 UNKNOWN 在 WHERE 里按不成立处理。统计“非某值”的记录时如果这列可空记得补一个OR phone IS NULL。3. 排序、分页与多表连接JOIN 家族一次理清单表查询写顺之后真实需求十有八九要跨表。这一章把ORDER BY、LIMIT的细节讲清再把 INNER、LEFT、USING、CROSS 四种连接和 UNION 放在一起对比重点落在“哪种连接会丢行、哪种会放大行数”这两个实际判断上。3.1 ORDER BY 多列排序与 LIMIT 分页语义排序支持多列优先级按书写顺序-- 先按州升序同州内按名字降序 SELECT * FROM customers ORDER BY state, first_name DESC;ASC是默认值可省略DESC只作用于紧邻的那一列。上面这条里state升序、first_name降序别误以为DESC对两列都生效。排序的稳定性依赖数据库实现MySQL 在排序键有重复时不保证原始顺序需要确定性输出就把主键追加到排序列表末尾。LIMIT的分页写法是笔记里最容易记混的一处-- 只取前 3 条 SELECT * FROM customers LIMIT 3; -- 跳过前 6 条再取 3 条即第 7~9 条 SELECT * FROM customers LIMIT 6, 3;LIMIT offset, count里第一个数是偏移量不是页码。做分页时第 n 页从 1 开始的偏移是(n-1) * pageSize。深分页offset 很大在 MySQL 上会先扫描再丢弃性能随偏移线性下降常见优化是先按主键范围过滤再 LIMIT或者用游标式分页记住上一页最后一条的排序键。3.2 INNER JOIN 与 LEFT JOIN丢行与补 NULL 的分界内连接只保留两表都能匹配上的行-- 只返回有订单的客户 SELECT * FROM customers c JOIN orders o ON c.customer_id o.customer_id;JOIN默认就是INNER JOIN。表别名c、o是为了在ON和SELECT里区分同名列两表都有customer_id时不加别名会报列名歧义。内连接的结果行数等于匹配组合数如果一个客户有 5 张订单这个客户就会出现 5 次——做客户数统计时直接COUNT(*)会得到订单数而不是客户数得用COUNT(DISTINCT c.customer_id)。左外连接保留左表全部行右表匹配不上就补 NULL-- 返回所有客户没有订单的客户订单列显示 NULL SELECT * FROM customers c LEFT JOIN orders o ON c.customer_id o.customer_id;判断“哪些客户没下过单”就在这条语句上加WHERE o.customer_id IS NULL。这里有个高频翻车点如果过滤条件写成WHERE o.amount 100那些没订单的客户因为o.amount是 NULL、条件不成立而被过滤掉LEFT JOIN 实际退化成了 INNER JOIN。要保留左表全部行又对右表加条件条件必须放在ON里而不是WHERE里-- 保留所有客户只关联金额大于 100 的订单 SELECT * FROM customers c LEFT JOIN orders o ON c.customer_id o.customer_id AND o.amount 100;ON决定怎么匹配WHERE决定匹配完留哪些行这个分工是理解外连接的核心。3.3 USING、CROSS JOIN 与 UNION 的适用边界当两表关联列名完全相同时USING可以省掉ON里的等值条件-- 关联列都叫 customer_id 时的简写 SELECT * FROM customers c JOIN orders o USING (customer_id);USING只适用于等值连接且列名一致输出结果里该列只出现一次而ON写法下两表的同名列都会保留。跨表列名不一致、或者需要非等值连接如ON o.amount BETWEEN ...时只能用ON。交叉连接生成笛卡尔积笔记的例子是颜色和尺码的所有组合-- 每种颜色配每种尺码行数 颜色数 × 尺码数 SELECT * FROM colors CROSS JOIN sizes;CROSS JOIN没有连接条件行数是两表行数相乘。它适合生成组合维度规格矩阵、排班组合但两张稍大的表一交叉就是灾难误写漏掉ON条件的 JOIN 在 MySQL 里也会退化成笛卡尔积看到结果行数暴涨先检查连接条件。UNION把多个结果集纵向拼起来-- 客户和潜在客户合并成一张名单 SELECT name, address FROM customers UNION SELECT name, address FROM clients;UNION默认去重会做一次排序或哈希数据量大时开销明显确定没有重复或允许重复时用UNION ALL更快。参与合并的各 SELECT 列数必须相同、对应列类型兼容列名以第一个 SELECT 为准。3.4 INSERT 的三种写法与 DEFAULT 的作用写入数据这块笔记给了单条和多条两种形态-- 单条插入未列出的列走默认值 INSERT INTO customers(first_name, phone, points) VALUES (Mosh, NULL, DEFAULT); -- 多条插入一次提交减少往返 INSERT INTO customers(first_name, phone, points) VALUES (Mosh, NULL, DEFAULT), (Bob, 1234, 10);DEFAULT关键字让该列取建表时定义的默认值前提是建表时确实写了DEFAULT否则取的是该列类型的隐式默认可空列是 NULL非空列在严格模式下直接报错。显式写NULL和写DEFAULT是两回事前者强制置空后者交给表定义。批量 INSERT 比逐条提交快是因为减少了网络往返和事务开销几千条以内一次提交通常没问题再大就要分批避免单条语句过大触发包大小限制。4. 避坑与排查这份速查表跑不通时的五个高频问题速查表的价值在于对照但示例语句直接抄进自己的库十有八九会撞上环境差异。下面五条是按“现象 → 原因 → 解决”整理的排查记录基本覆盖了从建库到 JOIN 的主要翻车点。现象执行USE sql_store报 Unknown database。原因示例库不存在笔记默认你已经有了课程配套库。解决按 2.1 的 DDL 自建或者把语句里的库名换成自己实际的库建完用SHOW TABLES确认表都在。现象WHERE state CA查出来是空集但表里明明有加州数据。原因state列存的是ca小写或者带了尾部空格CHAR定长补空格在某些比较规则下会出问题。解决先用SELECT DISTINCT state FROM customers看实际值比较时统一大小写必要时WHERE UPPER(state) CA但注意函数包列会让索引失效根治办法是入库时规范数据。现象LEFT JOIN 之后“没下过单的客户”查不出来。原因过滤条件写在了WHERE里而不是ON里NULL 行被条件筛掉外连接退化成内连接。解决把右表相关条件挪进ONWHERE只留o.customer_id IS NULL这类针对结果的判断。现象LIMIT 6, 3取到的不是第 6 到第 8 条。原因把第一个参数当成了起始行号。解决第一个参数是偏移量跳过 6 条从第 7 条开始取要取第 6 条起就写LIMIT 5, 3分页统一用(页码-1)*每页数计算偏移。现象REGEXP ^a在别的数据库上报错或行为不同。原因REGEXP是 MySQL 的运算符标准 SQL 里对应的是SIMILAR TO或各库自己的正则函数语法和转义规则都有差异。解决跨库时先确认目标库的正则支持方式MySQL 8 还提供REGEXP_LIKE()函数形式迁移时优先用函数写法便于替换。提示这份笔记的示例全部基于 MySQL 语法分号结尾、LIMIT offset, count、REGEXP运算符换到其他数据库前先过一遍方言差异别把 MySQL 写法当通用 SQL。5. 把速查表变成自己的查询模板库速查表看得再熟落到手上还是得变成能复用的东西。我的习惯是把这份笔记里的语句按“场景”重组成模板而不是按语法分类存放——语法分类适合学习场景分类适合干活。比如“按维度去重枚举”对应SELECT DISTINCT“分页列表”对应ORDER BY LIMIT“主表全保留 关联明细”对应LEFT JOIN ... ON ... AND ...“多来源合并名单”对应UNION ALL。每个模板留两三个占位符用的时候替换表名和列名比每次从零拼快得多。验证模板是否写对有个笨但有效的办法先用小LIMIT跑一遍看列和行数是否符合预期再逐步去掉LIMIT看总量最后用COUNT(*)和COUNT(DISTINCT 主键)对比两者不等就说明 JOIN 放大了行数。这个检查我几乎每次写多表查询都会走一遍尤其是涉及一对多关联的报表行数放大是最隐蔽的错误——数字看着合理口径已经错了。再往上一层可以把高频查询固化成视图把口径锁在数据库侧-- 客户订单汇总视图每个客户的订单数和总金额无订单记 0 CREATE VIEW customer_order_summary AS SELECT c.customer_id, c.first_name, COUNT(o.order_id) AS order_count, COALESCE(SUM(o.amount), 0) AS total_amount FROM customers c LEFT JOIN orders o ON c.customer_id o.customer_id GROUP BY c.customer_id, c.first_name;这里COUNT(o.order_id)而不是COUNT(*)是为了让无订单客户计为 0SUM对 NULL 求和结果是 NULL用COALESCE兜成 0。视图建好后报表查询直接SELECT * FROM customer_order_summary WHERE total_amount 0口径统一也省得每个人各写一版 JOIN。场景关键语法易错点枚举维度值SELECT DISTINCT col多列去重是组合去重区间过滤BETWEEN a AND b闭区间日期列含时分秒会漏边界前缀匹配LIKE x%%前置导致索引失效保留主表全部行LEFT JOIN ... ON ... AND ...右表条件误放 WHERE 退化为内连接合并结果集UNION ALL列数、类型需对齐UNION默认去重有开销分页LIMIT offset, count第一个参数是偏移不是页码从那以后我每次把这类速查笔记转成模板库都会强制走一遍“小 LIMIT 试跑 → 全量核对 → COUNT 对比”的流程宁可多花两分钟也不让口径错误流到下游报表里。希望这份拆解帮到你把这三小时的内容真正变成手上能用的东西。本文还有配套的精品资源点击获取