
拿订单表练手的朋友十有八九第一句SQL写的是SELECT * FROM orders然后发现查出来的数据多到无处下手。真正筛选出“超过1000元的订单”“上个月注册的用户”“标题里带‘教程’二字的文章”靠的都是WHERE子句。这篇就是写给零基础入门者的MySQL语法教学不讲高深理论只说怎么把筛选条件写对、写稳。WHERE子句是SQL里使用频率最高的语法之一它管的事只有一件从表里挑出符合条件的行。你写SELECT决定看哪些列写WHERE决定看哪些行。两者组合起来才算真正会用查询。本文会用最简单的例子、最直白的话把比较运算、逻辑组合、模糊匹配、空值判断以及新手必踩的几个坑一次讲清楚。适合刚装上MySQL、刚看完建表语法正准备写第一个查询的你。1. 零基础必看WHERE子句是什么为什么数据库离不开它1.1 从一张“全班成绩表”看懂筛选逻辑先放下晦涩的术语。假设你手里有一张Excel表里面是全班50个人的姓名、语文成绩、数学成绩。你现在想回答一个问题谁数学考了90分以上正常人不会把整张表从头到尾念一遍你会盯着“数学成绩”这一列挨个判断大于90还是不大于90然后把符合条件的名字抄出来。数据库做的也是这件事。WHERE子句就是告诉数据库“按这个条件把表中每一行都过一遍通过判断的留下不通过的丢掉。”表很大也没关系这恰恰是数据库存在的意义——它最擅长高速干这种重复判断。在SQL里这条查询长这样SELECT name, math_score FROM students WHERE math_score 90;逐词拆解一下SELECT name, math_score我要姓名和数学成绩这两列FROM students数据在students这张表里WHERE math_score 90只要数学成绩大于90的行执行逻辑上数据库先把FROM students定位到表然后用WHERE逐行判断“math_score 90”成立不成立成立的行进入下一步最后SELECT从这些行里取出指定列。很多人在初学阶段容易纠结SELECT和WHERE谁先执行记住一个简单结论先有行后有列。先把哪些行留下定下来再决定从这些行里取哪些列。1.2 WHERE子句在整个查询语句中的位置与基本语法结构WHERE的位置很固定它永远跟在FROM后面在GROUP BY、HAVING、ORDER BY、LIMIT的前面。一个完整的查询语句骨架如下SELECT 列1, 列2 FROM 表名 WHERE 条件 GROUP BY 分组字段 HAVING 分组后的筛选条件 ORDER BY 排序字段 LIMIT 条数;初学阶段不用管后面那几个但你必须把WHERE的位置焊死在脑子里它一定是FROM之后、ORDER BY之前。如果写成SELECT name FROM students ORDER BY math_score DESC WHERE math_score 90MySQL会直接报语法错误。提示有人会问为什么WHERE不能放在SELECT前面这是个好问题。从英语语感上“WHERE”好像应该先写但SQL是结构化语言它要求先声明数据来源FROM再声明筛选条件WHERE最后声明输出SELECT的执行顺序虽然在书写上先出现但逻辑执行在最后一步。刚开始不习惯很正常多写几条查询这个顺序自然就刻进肌肉记忆了。WHERE后面的核心是一个“条件表达式”。条件表达式的计算结果只有两种真TRUE或假FALSE。数据库对每一行计算这个表达式结果为真的行留下为假或未知NULL相关的行排除。理解“条件就是真/假判断”这一点比死记任何语法都有用。1.3 WHERE子句到底能解决什么实际问题在实际开发和个人项目中WHERE子句的使用场景比你想的多得多。举几个最典型的例子用户模块WHERE status 1查出所有启用的账号WHERE created_at 2024-01-01查出今年新增的用户订单模块WHERE total_amount 1000筛选大额订单WHERE pay_status paid筛选已支付订单内容模块WHERE title LIKE %教程%搜索标题包含“教程”的文章WHERE category_id IN (1,2,3)查出多个分类下的内容数据清理WHERE deleted_at IS NULL查出未删除的数据逻辑删除场景必备没有WHERE的SQL基本等于把整张表搬出来让你用肉眼找数据。一旦表里有几万行甚至几百万行人力完全无力应对。所以WHERE不是你“以后会用到”的语法而是从你写第二条SQL开始就离不开的东西。2. 最常用的筛选条件比较运算与范围判断2.1 六种比较运算符一次记牢WHERE最基础的操作就是比较。拿一个字段和一个值比大小、比相等、比不等MySQL支持以下运算符运算符含义示例实际查询效果等于WHERE name 张三只查张三!或不等于WHERE status ! 0状态不是0的行大于WHERE price 100价格大于100小于WHERE stock 10库存小于10大于等于WHERE age 18年龄大于等于18小于等于WHERE score 60分数小于等于60这里面有两个细节新手很容易忽略。第一!和是等价的数据库都认用哪个纯看个人习惯我一般用!因为敲起来顺手。第二是判断相等不是赋值。很多从编程语言转过来的朋友一开始会把WHERE name 张三理解成“把name设为张三”千万不要混条件判断里的单等号就是比较不产生任何写入动作。还有一个每天都会踩的坑字符串比较要加单引号数字比较不要加引号。-- 正确写法 WHERE name 张三 WHERE age 18 -- 错误的写法不影响报错但影响效率 WHERE name 张三 -- 数字列被当成了列名或函数名大概率报错 WHERE age 18 -- MySQL会做隐式转换能用但不推荐长期这么写注意数字列和字符串比较时MySQL会尝试把字符串转成数字。比如age 18会先把18转成18再比较结果没错但一旦字符串里混入非数字字符转换规则就会变得诡异。我的习惯是字段是啥类型比较值就写啥类型不偷懒。2.2 数字范围查询BETWEEN AND的正确打开方式想查“价格在50到100之间”的商品最直观的写法是WHERE price 50 AND price 100这样写完全没问题逻辑清晰任何数据库都支持。但MySQL还有一个更简洁的写法BETWEEN ... AND ...WHERE price BETWEEN 50 AND 100这两条SQL的结果一模一样。这里必须强调一个重点BETWEEN AND是闭区间包含了边界值。BETWEEN 50 AND 100实际等于price 50 AND price 10050和100都会被查出来。如果业务上需要“不包含多少元以下”那就别用BETWEEN回到普通比较运算符慢慢写。还有一个反向操作NOT BETWEEN AND语义也好懂不在某个区间内的行。WHERE age NOT BETWEEN 18 AND 60这条能查出18岁以下和60岁以上的人注意它是“或”的逻辑而不是“且”。2.3 日期和时间的筛选零基础最容易忽视的细节日期筛选说难不难说简单也有不少细节。先记住MySQL日期最常用的三种写法-- 写法一直接比较日期字符串 WHERE created_at 2024-01-01 -- 写法二精确到某一天 WHERE DATE(created_at) 2024-01-01 -- 写法三查最近7天 WHERE created_at DATE_SUB(CURDATE(), INTERVAL 7 DAY)新手第一个问题是日期能不能用直接查可以前提是你知道这个日期的存储格式。如果created_at是DATETIME类型存的值是2024-01-01 14:30:00你直接写created_at 2024-01-01查不到任何数据因为MySQL在比较时会拿2024-01-01 14:30:00和2024-01-01 00:00:00比当然不相等。想查某一天的所有记录最推荐的做法是范围查询WHERE created_at 2024-01-01 AND created_at 2024-01-02这样能把1月1日零点到1月1日23点59分59秒的所有记录都包含进来同时不会误伤1月2日的数据。如果用DATE(created_at) 2024-01-01也能查但DATE()函数会包住字段导致索引失效数据量一大速度会肉眼可见地变慢。初学阶段可以先不管索引但从第一天就养成“能不用函数包字段就不用”的习惯后面会省很多事。3. 多条件组合AND、OR、IN、LIKE怎么配合才不出错3.1 AND与OR的执行优先级为什么会查出意外结果实际业务里很少只有一个筛选条件。“找出上海地区且订单金额超过1000元的用户”要两个条件同时成立“找出标题包含SQL或者包含MySQL的文章”要任选一个成立。前者用AND后者用OR-- 两个条件同时满足 WHERE city 上海 AND order_amount 1000 -- 两个条件满足其一即可 WHERE title LIKE %SQL% OR title LIKE %MySQL%AND和OR单独用都简单一旦混用坑就来了。看这条SQLWHERE city 上海 OR city 北京 AND order_amount 1000你觉得它表达的是什么如果从前往后读很多人会理解成“城市是上海或者城市是北京且金额大于1000”。实际上MySQL也是这么执行的。AND的优先级高于OR所以这条SQL的真实含义是城市为上海的用户金额不限或者城市为北京且金额大于1000的用户。如果你本意是“城市是上海或北京且金额大于1000”必须加括号WHERE (city 上海 OR city 北京) AND order_amount 1000注意括号不是可选项是必需品。我在实际看别人SQL时凡是AND和OR混用没加括号的我都会多问一句“你到底想查啥”。加括号不只是为了改变优先级更是为了让人一眼看懂你的意图。代码是写给别人看的SQL也是。3.2 IN和NOT IN列出一组候选值最省事的办法查“北京、上海、广州三个城市的订单”用OR要写三遍city ...又长又容易漏。MySQL提供了IN语法WHERE city IN (北京, 上海, 广州)这等价于WHERE city 北京 OR city 上海 OR city 广州少写两遍而且后续想加成都直接在括号里加逗号字符串就行维护成本极低。反过来想排除这几个城市用NOT INWHERE city NOT IN (北京, 上海, 广州)有一点必须提醒IN列表里的值是“任一匹配”不是“全部匹配”。WHERE city IN (北京, 上海)永远不会匹配到一个同时叫“北京上海”的城市因为它判断的是字段值是否等于列表中的某一个值不是字段值是否包含列表中的所有值。这个逻辑看着简单但我在带新人时发现总有人把IN理解成模糊匹配导致查不出数据。3.3 LIKE模糊查询百分号和下划线的区别LIKE是搜索场景的顶梁柱。它的作用是做模糊匹配配合两个通配符使用%匹配任意多个字符包括零个字符_匹配任意一个字符具体用法看几个例子-- 标题以“SQL”开头 WHERE title LIKE SQL% -- 标题以“教程”结尾 WHERE title LIKE %教程 -- 标题任意位置包含“MySQL” WHERE title LIKE %MySQL% -- 标题第二个字符是“a” WHERE title LIKE _a%四个例子各说明一种场景前三个用的%最常见第四个_用来限定“一个字符”实际项目里用得少一些但理解它有助于读懂别人代码。初学LIKE最常犯的错是忘了写通配符。WHERE title LIKE MySQL不是模糊匹配它等价于WHERE title MySQL完全匹配才会返回。如果业务需求是“包含”百分号必须写而且常写两边%MySQL%。提示当搜索的值本身包含%或_时需要转义。比如你想查标题里带“50%折扣”的文章直接写LIKE %50%折扣%会把任意包含“50”、再间隔任意字符、再包含“折扣”的标题都查出来结果远超预期。正确做法是用转义符WHERE title LIKE %50\%折扣%MySQL里反斜杠\是默认转义符这个语法虽然不常用但遇到就搜不出来的时候能救命。3.4 组合实战一个带AND、OR、IN、LIKE的真实查询把前面所有内容串起来看一个稍微完整点的场景。假设用户表users里有一个备注字段remark你想找出年龄大于18岁城市是北京、上海、广州中的一个备注里包含“VIP”或“会员”但不能是名字叫“测试”的用户这条SQL的完整写法是SELECT id, name, city, age, remark FROM users WHERE age 18 AND city IN (北京, 上海, 广州) AND (remark LIKE %VIP% OR remark LIKE %会员%) AND name ! 测试;拆开看每条都没难度合在一起全是对运算符熟练度的考验。这种写法在真实项目里非常常见一个WHERE后面挂四五条条件有等值判断、范围判断、IN、LIKE、逻辑组合。你能一眼看懂它说明组合条件已经过关了。4. 空值判断IS NULL和IS NOT NULL是新手重灾区4.1 NULL是什么为什么不能用 NULL字段的值可以是空值NULL。NULL在数据库里是一个很特殊的存在它代表“没有值”“未知值”不是空字符串也不是数字0。这三个东西必须区分清楚NULL从未填写过不知道是什么空字符串填写了但内容为空0一个真实存在的数字区分“不知道”和“空字符串”在业务上意义重大。比如用户表的nickname字段NULL代表用户从没设置过昵称代表用户把昵称清空了两种状态对应的产品逻辑可能完全不同。新手最大的坑在于想查“昵称没填过的用户”会本能地写-- 错误写法永远查不出数据 WHERE nickname NULL为什么前面说过条件判断的结果必须是真或假但NULL NULL的结果是NULL未知不是真。数据库的逻辑是条件为“未知”也视为不成立所以这一行被排除了。NULL和任何值比较包括NULL自己结果都是NULL永远不是真。4.2 正确的空值判断写法判断NULL只能用IS NULL和IS NOT NULL-- 查昵称没填过的用户 SELECT * FROM users WHERE nickname IS NULL; -- 查昵称已经填过的用户 SELECT * FROM users WHERE nickname IS NOT NULL;这两条是空值判断的唯二标准写法。再强调一遍IS NULL不是 NULLIS NOT NULL也不是! NULL。你写了! NULL执行不会报错但结果一定是空而且你很难第一时间意识到问题出在语法而不是数据上。4.3 空字符串与NULL一起判断的需求业务里经常出现一个字段既有NULL又有空字符串的情况。比如导入历史数据时有些行没值有些行导入成了空字符串。你想把两种情况都查出来可以这样WHERE nickname IS NULL OR nickname ;等价地可以用一个写起来更短但不那么好读的函数IFNULL配合判断不过初学阶段不建议搞花活老老实实用IS NULL OR 最稳妥。注意IS NULL不能和LIKE、IN这些运算符混着用。比如WHERE remark LIKE %%不会把NULL的remark查出来因为NULL LIKE %%的结果是NULL。如果你想查“备注包含任意内容”的正确写法是WHERE remark IS NOT NULL不要指望LIKE %%帮你兜底。4.4 空值在计算和排序中的连锁影响NULL不只影响查询筛选还会影响排序和计算。比如SELECT * FROM products ORDER BY price DESC;如果有些行price是NULLMySQL默认会把NULL排在最前面升序时或最后面降序时这行为在不同数据库里还不一样。更麻烦的是在WHERE里做加减乘除时任何数值和NULL运算结果都是NULLWHERE total * 0.9 100凡是total为NULL的行total * 0.9也是NULL判断结果为NULL行被排除。这就导致“打折后超过100的商品”查不出来NULL价格的商品哪怕它打折后真的超过100。处理这类问题的正规做法是先用IS NOT NULL过滤或者在计算时用IFNULL把NULL替换成默认值但初学阶段先理解NULL的影响范围就足够了。5. 实战演练从建表到写出完整查询的完整流程5.1 准备一张学生表带着数据一步步调试纸上谈兵没意思直接动手。先建一张学生表插入几条测试数据然后用前面学的所有语法写查询。打开你的MySQL命令行或Navicat执行以下建表和插入语句CREATE TABLE students ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), city VARCHAR(50), age INT, score DECIMAL(5,2), enrollment_date DATE, remark VARCHAR(255) ); INSERT INTO students (name, city, age, score, enrollment_date, remark) VALUES (张三, 北京, 20, 88.5, 2023-09-01, 优秀学生), (李四, 上海, 19, 72.0, 2023-09-02, NULL), (王五, 广州, 22, 91.0, 2022-09-01, 学生会成员), (赵六, 北京, 18, 65.5, 2024-02-15, 需要关注), (钱七, 深圳, 21, 83.0, 2023-09-01, ), (孙八, 上海, 20, NULL, 2023-10-11, 缺考一门);这张表有6条数据包含了字符串、数字、小数、日期和NULL字段基本能覆盖前面讲的所有场景。如果你用的MySQL版本支持CREATE TABLE和INSERT一次性执行MySQL 8.0支持直接全部选中执行即可。5.2 六条查询由简到繁逐条对照结果验证现在开始练习。每一条都先自己想答案再执行对比。查询1北京的学生有哪些SELECT name, city FROM students WHERE city 北京;结果应该是张三和赵六。这个没什么悬念简单等值判断。查询2成绩大于等于80分的学生SELECT name, score FROM students WHERE score 80;结果是张三、王五、钱七。注意孙八的成绩是NULL它不会被算进来因为NULL 80的结果是NULL。这是个非常典型的NULL过滤场景理解之后以后看到成绩统计里少人第一反应就该是“是不是有人的成绩是NULL”。查询3北京、上海、广州的学生SELECT name, city FROM students WHERE city IN (北京, 上海, 广州);结果是张三、李四、王五、赵六、孙八深圳的钱七被排除。如果之前没用过IN这条就是最好的上手练习。查询42023年入学的学生SELECT name, enrollment_date FROM students WHERE enrollment_date 2023-01-01 AND enrollment_date 2024-01-01;结果是2023年入学的张三、李四、王五、钱七、孙八。用和组合是日期范围查询最稳的方式不用函数索引也能正常使用。查询5备注包含“学生”字样或备注为空的SELECT name, remark FROM students WHERE remark LIKE %学生% OR remark IS NULL;结果是张三、李四、王五。外行人可能以为钱七的空字符串也会被查出来但实际上不是NULL LIKE %学生%是假所以被排除了。这个例子就是为了强化一个认知空字符串和NULL是两码事。查询6成绩不为空且不是深圳的学生SELECT name, city, score FROM students WHERE score IS NOT NULL AND city ! 深圳;结果是张三、李四、王五、赵六、孙八被排除成绩NULL钱七被排除深圳。剩下四个人的成绩都不是NULL城市也不是深圳。这条综合了IS NOT NULL和不等于判断是前面所有知识的汇总。我把建表、插数据、查询的过程完整走一遍发现实战和看语法最大的区别是你得自己判断结果对不对而不是等语法执行完就万事大吉。建议你跑完每条查询都把结果和预期对一遍重点看有没有多出意料之外的行或者少了本该出现的行这些偏差往往就是你对某个运算符理解有误的信号。6. 新手高频报错与排查思路能少走很多弯路6.1 三种最常见的语法错误一报错就心慌先看这三点写WHERE子句时新手容易在三种错误上反复翻车。第一种是字符串漏写单引号-- 报错示例 SELECT * FROM students WHERE city 北京; -- 正确 SELECT * FROM students WHERE city 北京;看到Unknown column 北京这类报错基本就是字符串没加引号MySQL把“北京”当成列名去解析了。记一个规律凡是报错里出现“Unknown column”并且指向的是中文或英文字符串十有八九是引号问题。第二种是关键字用错或位置写错比如把WHERE写在ORDER BY后面MySQL直接报语法错误提示位置在WHERE附近。遇到这类报错把SQL从头到尾检查一遍顺序FROM在前WHERE紧跟ORDER BY最后。第三种是条件漏写字段比如WHERE 18这种错误MySQL会报Unknown column 18之类的提示。条件表达式必须是一个完整结构字段、运算符、值三者缺一不可。6.2 逻辑错误比语法错误更危险三条经验帮你快速定位语法错误报错明显一个红叉甩在脸上改起来反倒简单。真正磨人的是“SQL能跑但结果不对”这类逻辑错误排查起来需要思路。经验一先缩小范围。当你觉得结果莫名其妙时先去掉一半条件只查原始数据然后逐步加条件每次加一个跑一次对比一次。比如你写了四个条件的结果不对就先只跑第一个条件看是不是它的问题再叠加第二个。这条方法看起来笨但确实是我用的最多的排查手段。经验二重点检查NULL相关判断。结果少行第一个怀疑对象就是NULL。score 80不会包含NULL成绩remark ! 不会包含NULL备注city IN (北京,上海)遇到NULL城市也会把它排除。如果你的字段允许NULL而你又没写IS NULL OR结果少一行两行是很正常的。经验三检查AND/OR优先级。用了三个以上条件括号写没写A OR B AND C和(A OR B) AND C结果是不同的。拿不准就在每个组合外面加括号既保证逻辑对也方便别人读。6.3 一个数据量变大后必须了解的效率隐患初学阶段用几百条数据跑查询感觉啥都快根本不需要考虑性能。但一旦表里数据到几十万行你会发现同样一条SQL有时候几百毫秒有时候好几秒差别就在WHERE条件的写法上。这里只提一个最核心的注意点别在WHERE里对字段做函数运算。比如刚才提过的-- 高效写法不会包住字段 WHERE enrollment_date 2023-01-01 AND enrollment_date 2024-01-01 -- 低效写法字段被函数包住索引失效 WHERE YEAR(enrollment_date) 2023两个写法结果一样但第二个会让MySQL放弃索引强制全表扫描。初学者可以不懂索引原理但记住“字段不能用函数包”这句话能帮你少踩一个以后必踩的坑。提示一般查询课讲到数据量、索引很多人觉得离自己很远。实际上你如果以后要做个人项目、写后台管理数据到几万行是很快的事。我从第一天就建议你按“会长期使用的SQL”标准来写养成不加多余函数、不写SELECT *明确需要的字段的习惯这种“提前量”会在关键时刻救你一把。7. 写在最后的个人经验带过不少从零开始学MySQL的朋友我发现一个规律能把WHERE用好的不是那种一遍看完语法、觉得自己都会了的人而是肯一行行敲代码、每条查询都核对结果的人。筛选就是SQL的基本功基本功不牢后面学多表查询、分组统计、窗口函数全部都会像在沙地上盖楼。我自己写WHERE的经验很简单先跟业务确认“我要挑出什么样的行”再动手写条件。条件多的时候先在纸上列出来哪些是“且”、哪些是“或”、哪些要排除然后对应成AND、OR、!。这样写出来的SQL准确率远比边想边写高得多。现在已经看到这里了建议你打开MySQL把文中的学生表建起来六条查询逐条跑一遍再自己加几条条件组合着练。SQL这东西看十遍不如自己写一遍。