MySQL SQL速查表实战指南:从建库到JOIN避坑 简介这份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轻量易存适合作为日常查询速查手册或课程配套讲义使用。目前已有979人学习适合需要快速上手数据库查询、巩固语法细节的读者收藏参考。1. 一份三小时速查表为什么值得单独拆一遍很多人学 SQL 的路径是打开教程、跟着敲、关掉页面、三天后忘光。问题不在教程在于缺一份能随时翻、能直接抄、能对照排错的速查表。这份 Mosh 的 SQL Cheat Sheet 就是干这个的——它把三小时入门课里真正高频用到的语法压成十几页从SELECT、WHERE一路覆盖到JOIN、UNION、INSERT每个知识点配一条能跑的示例语句。它不教你数据库设计也不讲索引优化定位很明确给刚上手 MySQL 的人一张“语法地图”让你在写查询时不用反复回看视频。适合谁写过一两次SELECT但一遇到多表关联就卡壳的人以及需要一份离线速查资料随时对照的从业者。下面我按“先跑通环境、再逐块吃透语法、最后避开常见翻车点”的顺序拆。2. 先把示例库跑起来建库、导数据与第一条查询速查表里所有示例都基于sql_store这个库表有customers、orders、colors、sizes、clients。你手上如果没有这套数据直接抄语句会全部报“表不存在”。所以第一步不是背语法是把环境对齐。2.1 建库与最小数据集常见做法是本地装一个 MySQL 8.x然后用下面这段脚本把速查表涉及的表和字段建出来。字段名严格对齐速查表里的customer_id、first_name、state、points、birthdate、phone否则后面USING (customer_id)这类写法会对不上。-- 建库并切换 CREATE DATABASE IF NOT EXISTS sql_store; USE sql_store; -- 客户表覆盖速查表里 WHERE / ORDER BY / LIKE 用到的字段 CREATE TABLE customers ( customer_id INT PRIMARY KEY AUTO_INCREMENT, first_name VARCHAR(50), state CHAR(2), points INT DEFAULT 0, birthdate DATE, phone VARCHAR(20) ); -- 订单表用于 INNER / LEFT JOIN 演示 CREATE TABLE orders ( order_id INT PRIMARY KEY AUTO_INCREMENT, customer_id INT, order_date DATE, amount DECIMAL(10,2) ); -- 交叉连接演示用的两张小表 CREATE TABLE colors (color VARCHAR(20)); CREATE TABLE sizes (size VARCHAR(20)); -- 第二张客户表用于 UNION 演示 CREATE TABLE clients ( name VARCHAR(50), address VARCHAR(100) );逻辑说明customers的主键叫customer_id这是关键——速查表里JOIN orders o ON c.customer_id o.customer_id和USING (customer_id)都依赖两张表同名字段。points给了DEFAULT 0对应后面INSERT里写DEFAULT的用法。state用CHAR(2)是因为示例里全是CA、VA、NY这种两字母缩写。参数上要注意AUTO_INCREMENT让主键自增插入时不用手写 idDECIMAL(10,2)存金额避免浮点误差。如果你用的是 PostgreSQLAUTO_INCREMENT要换成SERIALCHAR(2)行为基本一致但REGEXP要改成~这点后面避坑章会展开。2.2 灌入能触发所有语法的样本数据光建表不够数据得“有坑”才能验证语句。比如要测IS NULL就得有phone为空的记录要测LEFT JOIN返回无订单客户就得有客户在orders里查不到。-- 客户数据故意留一条 phone 为 NULL一条 points 在 100~200 之间 INSERT INTO customers (first_name, state, points, birthdate, phone) VALUES (Babak, CA, 1200, 1990-05-01, 555-0101), (Bobby, VA, 150, 1985-11-20, NULL), (Bryan, NY, 800, 1992-02-14, 555-0103), (Mosh, CA, 180, 1988-07-07, NULL), (Nancy, TX, 50, 1995-09-30, 555-0105); -- 订单数据只给部分客户下单制造 LEFT JOIN 的空匹配 INSERT INTO orders (customer_id, order_date, amount) VALUES (1, 2024-01-10, 99.00), (1, 2024-02-15, 45.50), (3, 2024-03-01, 200.00); INSERT INTO colors VALUES (red), (blue); INSERT INTO sizes VALUES (S), (M), (L); INSERT INTO clients VALUES (Alice, 1 Main St), (Bob, 2 Oak Ave);逻辑说明Bobby和Mosh的phone是NULL正好用来验证WHERE phone IS NULLBobby的points是 150落在BETWEEN 100 AND 200区间内Nancy没有订单LEFT JOIN时她的订单字段会是NULL。这样一份数据能把速查表里八成语句跑出预期结果。2.3 跑通第一条完整查询环境就绪后先跑速查表开头那条组合语句确认库、表、字段全对得上USE sql_store; SELECT * FROM customers WHERE state CA ORDER BY first_name LIMIT 3;这条语句同时用到了库切换、过滤、排序、截断四个动作。执行顺序在逻辑上是FROM→WHERE→SELECT→ORDER BY→LIMIT但书写顺序是SELECT在前。新手常误以为LIMIT先于ORDER BY生效实际上先排序再截断所以LIMIT 3拿到的是按first_name排完后的前三条。如果结果为空先查SELECT COUNT(*) FROM customers确认数据进去了再查state值有没有多余空格——CHAR(2)在某些配置下会补空格用WHERE TRIM(state) CA能验证。3. 过滤与排序WHERE、逻辑运算符、LIKE 与 REGEXP 的边界这一块是速查表里密度最高的部分也是最容易“看着会、写起来错”的地方。核心不是记语法是记每个运算符在什么数据上会给出反直觉结果。3.1 比较运算符与逻辑组合的优先级速查表列了、、、、、、!其中和!等价MySQL 两者都认。真正要小心的是AND和OR混用时的优先级AND先于OR求值。看这条-- 意图加州或纽约、且积分大于 500 的客户 SELECT * FROM customers WHERE state CA OR state NY AND points 500;因为AND优先级高实际等价于state CA OR (state NY AND points 500)加州客户不管积分多少都会被选出来。正确写法必须加括号SELECT * FROM customers WHERE (state CA OR state NY) AND points 500;参数说明括号改变求值顺序这是 SQL 里少数“加了就一定对”的地方。NOT同理速查表写WHERE NOT (birthdate 1990-01-01)括号不能省否则NOT只作用于紧邻的条件。我的习惯是只要语句里同时出现AND和OR无条件给每个逻辑组加括号宁可多打两个字符。3.2 IN、BETWEEN 与 NULL 的三处陷阱IN用来替代一长串ORWHERE state IN (VA,NY,CA)比三个OR清晰得多。BETWEEN 100 AND 200是闭区间包含两端这点和某些语言不同。真正的坑在NULL-- 错误直觉想找 phone 不是 555-0101 的客户 SELECT * FROM customers WHERE phone 555-0101;这条语句不会返回phone为NULL的行。原因是NULL表示“未知”任何与NULL的比较结果都是UNKNOWN而WHERE只保留结果为TRUE的行。要包含空值得显式写SELECT * FROM customers WHERE phone 555-0101 OR phone IS NULL;速查表里IS NULL单独成节就是这个原因。同理NOT IN的子查询里如果出现NULL整个结果会为空——这是血泪经验里排前三的翻车点后面避坑章细说。3.3 LIKE 与 REGEXP通配符和正则的分工LIKE只有两个元字符%匹配任意长度字符_匹配恰好一个字符。WHERE first_name LIKE b%找 b 开头LIKE _ob找三个字符且后两位是 ob。它简单、可移植几乎所有数据库都支持。REGEXP是 MySQL 的正则匹配速查表给了^开头、$结尾、|或、[abc]字符集、[a-d]范围。几个示例值得逐条对照-- 以 ey 或 on 结尾 SELECT * FROM customers WHERE first_name REGEXP ey$|on$; -- 以 my 开头或包含 se SELECT * FROM customers WHERE first_name REGEXP ^my|se; -- 包含 b且 b 后面紧跟 r 或 u SELECT * FROM customers WHERE first_name REGEXP b[ru];逻辑说明^my|se的优先级容易误读它等价于(^my)|(se)即“以 my 开头”或“任意位置含 se”不是“以 my 或 se 开头”。要表达后者得写^(my|se)。参数上REGEXP默认不区分大小写取决于排序规则collation如果库的collation是_bin结尾则区分。可移植性上REGEXP是 MySQL 方言PostgreSQL 用~SQL Server 没有原生正则换库时这段要重写——这也是为什么速查表把它和LIKE分开列LIKE才是跨库安全的那一个。3.4 ORDER BY 多列与 LIMIT 的偏移写法ORDER BY state, first_name DESC表示先按state升序state相同再按first_name降序。注意DESC只作用于紧邻的那一列不是整条ORDER BY。要两列都降序得写ORDER BY state DESC, first_name DESC。LIMIT有两种形式LIMIT 3取前三条LIMIT 6, 3表示跳过 6 条取 3 条等价于LIMIT 3 OFFSET 6。后者在分页里常用但要注意没有ORDER BY的LIMIT结果顺序是不确定的翻页时可能重复或漏行。分页查询必须带稳定排序通常用主键ORDER BY customer_id LIMIT 6, 3。4. 多表与写入JOIN 家族、UNION 和 INSERT 的实操细节单表查询写顺了真正的分水岭在多表。速查表把INNER JOIN、LEFT JOIN、USING、CROSS JOIN、UNION、INSERT放在一起篇幅不大但每条都是高频操作。4.1 INNER JOIN 与 LEFT JOIN 的结果集差异-- 内连接只返回有订单的客户 SELECT * FROM customers c JOIN orders o ON c.customer_id o.customer_id; -- 左连接所有客户都返回没订单的订单字段为 NULL SELECT * FROM customers c LEFT JOIN orders o ON c.customer_id o.customer_id;逻辑说明JOIN默认就是INNER JOIN只保留两表都能匹配上的行。用第 2 章的数据内连接只会出customer_id为 1 和 3 的客户1 有两条订单3 有一条Bobby、Mosh、Nancy全部消失。LEFT JOIN保留左表全部行右表匹配不上就填NULL所以Nancy会出现且order_id为NULL。参数上ON后面写连接条件别把过滤条件混进去。比如“只看 2024 年之后的订单”写在ON里和写在WHERE里对LEFT JOIN结果不同写在WHERE o.order_date 2024-01-01会把NULL行过滤掉LEFT JOIN退化成内连接写在ON里则保留无订单客户。这是面试和实战都爱考的点。4.2 USING 的适用条件与 CROSS JOIN 的用途USING (customer_id)是ON c.customer_id o.customer_id的简写前提是两张表的连接字段完全同名。速查表特意注明“If column names are exactly the same”。一旦字段名不同比如一边叫cust_idUSING直接报错只能退回ON。-- 字段同名时可用 SELECT * FROM customers c JOIN orders o USING (customer_id); -- 交叉连接颜色 × 尺码得到所有组合 SELECT * FROM colors CROSS JOIN sizes;CROSS JOIN返回两表的笛卡尔积2 种颜色 × 3 种尺码 6 行。它常被误认为“没用”实际在生成规格组合、日期维度补齐时很实用。注意它没有连接条件行数是两表行数相乘大表上误用会瞬间炸出百万行这是性能翻车的典型场景。4.3 UNION 去重与 INSERT 的 DEFAULT 写法UNION把两个结果集上下拼接并自动去重UNION ALL保留重复且更快。速查表示例SELECT name, address FROM customers UNION SELECT name, address FROM clients;两个SELECT的列数必须相同列名以第一个为准类型要兼容。如果customers里没有name、address字段第 2 章建表时确实没有这条要改成对应字段或先补列否则报“Unknown column”。这是照抄速查表最容易卡住的地方——速查表假设的表结构和你的不一定一致。INSERT部分有两个细节-- 单条插入points 用 DEFAULT 走建表时的默认值 0 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关键字让该列取建表时定义的默认值比手写0更能适应表结构变更。NULL要显式写不能留空占位。多值插入比逐条INSERT快一个数量级因为只解析一次语句、只提交一次事务批量导入时优先用这种写法。5. 避坑与排查照抄速查表最容易翻车的五个地方速查表是“语法索引”不是“可执行工程”。直接复制粘贴到自己的库下面五类问题出现频率最高。5.1 报错“Table doesnt exist”库没切或表名不对现象语句和速查表一字不差执行报ERROR 1146 (42S02): Table sql_store.customers doesnt exist。 原因速查表每条语句前都隐含USE sql_store;你当前连接的库不是它或者根本没建这套表。 解决先SELECT DATABASE();看当前库再SHOW TABLES;确认表在不在。没有就回第 2 章建表灌数据。养成习惯脚本开头永远写USE 库名;别依赖客户端默认库。5.2NOT IN遇到 NULL 返回空集现象WHERE customer_id NOT IN (SELECT customer_id FROM orders)明明有客户没下单结果却是 0 行。 原因子查询结果里只要有一个NULLNOT IN的整个比较就变成UNKNOWN所有行被过滤。 解决子查询加WHERE customer_id IS NOT NULL或改用NOT EXISTS。后者对NULL免疫是更稳的写法SELECT * FROM customers c WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id c.customer_id );5.3LEFT JOIN过滤条件写错位置导致退化现象本想保留所有客户加了订单日期过滤后无订单客户全没了。 原因过滤条件写在WHERE里NULL行不满足条件被剔除左连接退化成内连接。 解决把右表的过滤条件挪到ON子句。判断标准——想保留左表全部行右表条件进ON想过滤最终结果才进WHERE。5.4REGEXP换库直接失效现象本地 MySQL 跑得好好的正则查询迁到 PostgreSQL 报语法错误。 原因REGEXP是 MySQL 方言PostgreSQL 用~SQL Server 无原生正则。 解决跨库场景优先用LIKE确需正则时按目标库改写并在代码层做方言适配。速查表本身也把LIKE和REGEXP分列就是在暗示可移植性差异。5.5LIMIT分页无排序导致翻页重复现象LIMIT 0,10和LIMIT 10,10两页出现同一条记录。 原因没有ORDER BY时数据库不保证返回顺序两次查询的物理顺序可能不同。 解决分页必须带唯一且稳定的排序键通常用主键ORDER BY customer_id LIMIT 10 OFFSET 10。如果排序键有重复值再加一个唯一列做次级排序。6. 把速查表变成自己的三条验证习惯与一个进阶练法速查表最大的价值不是读是“改”。我拿到任何一份 cheat sheet第一件事是把它里面的每条语句在自己的库上跑一遍跑不通的标红跑通的补一条“反例”。具体做法给每个语法点准备一条“应该返回空”的查询比如WHERE phone NULL必须返回空因为应该用IS NULLWHERE points BETWEEN 200 AND 100也必须返回空区间反了。能预测出“空结果”的语句才算真懂了这个运算符的边界。第二个习惯是给JOIN系列建一张对照表把同一份数据在内连接、左连接、右连接下的行数和NULL分布记下来。以第 2 章数据为例查询类型返回行数无订单客户是否出现订单字段INNER JOIN3否全部有值LEFT JOIN5是无订单者为 NULLCROSS JOIN15不适用笛卡尔积行数对不上说明连接条件或数据有问题比盯着结果集肉眼找快得多。第三个习惯是拿EXPLAIN看执行计划。速查表不讲性能但你迟早会遇到“语法对、跑得慢”的查询。在语句前加EXPLAIN重点看type列出现ALL表示全表扫描ref或eq_ref表示走了索引。多表JOIN时如果驱动表选错行数会爆炸EXPLAIN的rows列能提前预警。进阶练法把速查表里的customers/orders换成三张表——客户、订单、订单明细然后只用速查表覆盖的语法不含窗口函数、CTE写出“每个客户最近一笔订单的金额”。这道题会逼你把LEFT JOIN、子查询、ORDER BYLIMIT组合起来用写完再对照速查表查漏比从头看视频快得多。从那以后我每次拿到新的语法速查资料都强制先跑“反例验证”再动手写业务查询——预测空结果比预测有结果更能暴露理解偏差。希望帮到你。本文还有配套的精品资源点击获取