☰
MySQL零基础入门:SELECT查询与WHERE条件筛选详解
2026/10/6 8:58:52 网站建设 项目流程

干了这么多年数据库,带过不少新人,发现大家学到查询这块最容易卡壳。前面几篇讲完安装、建库、建表,到这儿反而懵了:明明SELECT看起来就两个单词,怎么一到真项目里就写不对?这篇零基础第八篇,就是专门把"基础查询"这层窗户纸捅破的。

我会按照实际干活时的思路来讲,先搞清楚查询是查什么,再一步步拆解SELECT的各个零件。从最简单的全表查询,到带条件的精准筛选,再到排序,最后把我这些年踩过的坑一并端出来。你可以放心,这篇不需要任何基础,只要前面几篇照着做完、手头有个能跑的MySQL环境,就能跟着上手。

1. 查询之前,先把"查什么"这件事想明白

1.1 连接数据库并确认目标表

很多新手一上来就写SELECT,结果报错"Table doesn't exist",其实就是没搞清楚查询是发生在哪个"空间"里的。MySQL的逻辑结构很像是你家里的文件柜:数据库(Database)是文件柜,表(Table)是文件柜里的文件夹,行(Row)是文件夹里的一张张纸,列(Column)是纸上填写的项目,比如姓名、年龄、价格。

所以用查询前,第一步是得站在正确的文件柜前面。命令行里先登录:

mysql -u root -p

输入密码后,你就进入了一个MySQL的"大厅"。这时可以用这条命令看看大厅里摆了几个文件柜:

SHOW DATABASES;

找到自己建的那个库,比如叫shop,就进去:

USE shop;

再确认一下这个库里有哪些文件夹(表):

SHOW TABLES;

到这儿,你才真正具备了写查询的前提。我见过太多新手问"为什么我的SELECT报错",最后发现压根没执行USE,系统都不知道你要查哪个库。

1.2 理解行和列:查询的本质是"取行的子集、选列的子集"

清楚了库和表的结构后,再往深想一层:查询的本质其实就两件事——筛选行,挑选列。你想看哪些行(满足什么条件的记录),你想看这些行的哪些列(每个记录的哪些属性)。SELECT后面跟的是"要哪些列",WHERE后面跟的是"要哪些行"。

我习惯让初学者把查询理解成"从一张Excel表里做筛选"。你打开一个大表,第一反应肯定是先看有多少行、多少列,然后按照某个条件筛选出关心的数据,再隐藏掉不需要的列。SQL查询干的也是同样的事,只是把鼠标操作变成了文字指令。

为了后续演示方便,假设我们在shop库里有一张users表,里面存着用户信息:

idnameagecitybalancecreated_at
1张三25北京100.002024-01-05
2李四30上海200.002024-03-10
3王五25北京50.002024-06-15
4赵六28广州0.002024-08-20
5孙七35NULL88.002024-11-02

后面所有例子都围绕这张表进行。

2. SELECT基础语法:先把数据"取出来"再说

2.1 最简单的查询:SELECT * 与指定列查询

新手的第一句SQL,基本都是这样:

SELECT * FROM users;

*表示"所有列",这句话的意思是:把users表里所有行、所有列的数据全部拿出来。这种写法在学习阶段完全没问题,因为你想快速看看表里有什么。但在实际工作中要养成好习惯,尽量不要一上来就SELECT *。原因主要有两点:

一是浪费资源。如果一张表有几十个字段,而你的业务只需要其中两三个,那查出来的额外数据在网络传输和内存占用上都是白花花的成本。数据量大起来以后,差别非常明显。

二是可读性差。别人看你的代码,根本不知道你到底需要哪些数据。后面接上JOIN、子查询时,满屏的*会让逻辑变得一团乱。

更规范的写法是指定列名:

SELECT name, age FROM users;

这条语句只取name和age两列。数据库执行时也只需要读取这两列的数据,效率更高,结果也更清爽。

2.2 给列起别名:让查询结果"说人话"

有时候表里的字段名比较简写,比如created_at你可能想显示成"创建时间",这就用到别名:

SELECT name AS 姓名, age AS 年龄 FROM users;

AS后面跟的就是别名。注意,中文别名在某些环境下直接写没问题,在另一些环境下需要加引号:

SELECT name AS '姓名', age AS '年龄' FROM users;

我个人的习惯是统一加单引号,避免因为版本、字符集差异导致莫名报错。别名不会修改表里的真实字段名,它只是查询结果展示时的一个"临时名字"而已。

2.3 去重查询:DISTINCT的典型场景

再来看一个经常被问到的功能:去重。比如你想知道users表里共有哪些城市(重复的不算):

SELECT DISTINCT city FROM users;

执行结果是北京、上海、广州、NULL,每个城市只出现一次。DISTINCT的作用就是"把查询结果里完全相同的行合并成一行"。它跟后面的多个列组合使用时会有些微妙的地方,比如:

SELECT DISTINCT city, age FROM users;

此时去重逻辑是"城市和年龄两个字段组合在一起完全相同"才算重复。北京出现了两次,但因为年龄都是25,所以会被去重成一列。这个行为很容易让新手误解,以为DISTINCT只是对第一个字段去重,实际不是的。

需要留意的是,DISTINCT本身是有开销的,它要求MySQL对所有返回的数据做一次排序或哈希去重。表越大,代价越高。能用WHERE提前过滤掉的数据,就别扔到DISTINCT里处理。

2.4 查询常量与表达式:SELECT 不只是查表

还有一个基础但重要的点:SELECT不一定非得跟FROM。你可以直接在MySQL里当计算器用:

SELECT 1 + 1; SELECT NOW(); SELECT 'Hello MySQL';

这在调试的时候特别有用。想确认当前数据库时间、计算个简单数值,不用打开计算器,直接在MySQL里搞定就行。实际工作中,这类句子的价值更多体现在"验证某个函数行为"上,而不在业务查询里。

3. WHERE条件筛选:让查询精确命中目标数据

查全表只是热身,真正干活得靠条件筛选。WHERE子句就是用来指定"我要哪些行"的。

3.1 比较运算:等于、不等于、大于、小于

最基础的筛选方式是拿字段跟一个值做比较。比如查出年龄等于25的用户:

SELECT name, age FROM users WHERE age = 25;

这里的=是"等于",跟编程语言里的赋值是完全两码事,可别搞混了。再看不等于,可以用!=或<>,两者等效:

SELECT name, age FROM users WHERE age <> 25;

大于、小于、大于等于、小于等于也都一样:

SELECT name, age FROM users WHERE age >= 28; SELECT name, age FROM users WHERE age <= 25;

这里有个新手必踩的坑:字符串比较必须加引号。比如:

SELECT * FROM users WHERE city = 北京;

这句会直接报错,因为MySQL把没加引号的北京当成列名去解析了。正确的写法是:

SELECT * FROM users WHERE city = '北京';

3.2 组合条件:AND和OR的逻辑关系

实际需求很少只有一个条件,更多是"年龄大于25 并且 城市是北京"这种组合。此时就要用AND和OR。AND表示必须同时满足,OR表示满足其中一个即可。

SELECT * FROM users WHERE age > 25 AND city = '北京'; SELECT * FROM users WHERE age > 25 OR city = '北京';

第一种情况只会返回北京且年龄大于25的人;第二种情况会把"所有年龄大于25的人"加"所有北京的人"一起返回,满足一边就会被选中。

问题来了:如果条件里既有AND又有OR,谁说了算?

SELECT * FROM users WHERE age > 25 OR city = '北京' AND balance > 0;

很多人的第一反应是"从左往右算"。但MySQL里AND的优先级比OR高,相当于隐式地给city = '北京' AND balance > 0加了括号。也就是说,上面语句实际含义是:

SELECT * FROM users WHERE age > 25 OR (city = '北京' AND balance > 0);

这才是最容易出bug的地方。我的经验是:只要混用了AND和OR,一律用括号把逻辑关系写清楚。括号不仅能改变优先级,更重要的是让读代码的人一眼看懂你的意图,省去猜谜的负担。

3.3 IN与NOT IN:离散值的快速匹配

如果条件变成"城市是北京或上海或广州",你可以用一连串OR:

SELECT * FROM users WHERE city = '北京' OR city = '上海' OR city = '广州';

这么写虽然对,但很啰嗦,还容易漏掉条件。更干净的方式是IN:

SELECT * FROM users WHERE city IN ('北京', '上海', '广州');

IN后面跟一个值列表,只要字段值在这个列表里,行就会被选中。反过来,想排除这些城市,就用NOT IN:

SELECT * FROM users WHERE city NOT IN ('北京', '上海', '广州');

有一个细节必须讲清楚:如果IN列表里含有NULL,那NOT IN的结果会跟你想的完全不一样。比如:

SELECT * FROM users WHERE city NOT IN ('北京', NULL);

这条语句不会返回任何行。原因跟SQL的三值逻辑(TRUE、FALSE、UNKNOWN)有关:当city跟NULL比较时,结果不是"不等于",而是"未知"(UNKNOWN)。UNKNOWN在WHERE里会被当作不满足条件处理。所以包含NULL的比较要格外小心,宁可先用后面讲的IS NULL把空值单独拎出来处理。

3.4 BETWEEN:范围查询的便捷写法

想找出余额在50到200之间的用户,用比较运算符也能写:

SELECT * FROM users WHERE balance >= 50 AND balance <= 200;

也可以用BETWEEN:

SELECT * FROM users WHERE balance BETWEEN 50 AND 200;

这里必须提醒一句:BETWEEN是闭区间,即包含两端值。50和200都会被查出来。很多人以为是开区间,写着写着就漏数据了,这是非常典型的偏差。如果确实不想包含边界,还是老老实实写>、<。

BETWEEN不只适用于数值,日期也适用:

SELECT * FROM users WHERE created_at BETWEEN '2024-01-01' AND '2024-06-30';

但日期边界同样要小心。这里查的是created_at,如果字段是DATETIME类型,2024-06-30默认相当于2024-06-30 00:00:00,那2024-06-30 08:00:00的数据就查不到。更稳妥的做法是用>=和<组合:

SELECT * FROM users WHERE created_at >= '2024-01-01' AND created_at < '2024-07-01';

这个写法能准确覆盖整个6月。

3.5 LIKE模糊查询:%和_的用法

"模糊查询"是基础查询里非常有代表性的需求。比如你想找所有姓"张"的用户,完全相等匹配肯定不适用,这时候要用LIKE:

SELECT * FROM users WHERE name LIKE '张%';

%是通配符,代表"任意长度的一串字符"。'张%'表示"以张开头",后面跟啥都行。另一种常见场景是找包含某关键词的人:

SELECT * FROM users WHERE name LIKE '%三%';

意思是"名字里有个'三'"。注意,%能匹配零个或多个字符,所以'张%'也匹配 "张" 本身。

除了%,还有一个下划线_,它只匹配一个字符。比如:

SELECT * FROM users WHERE name LIKE '张_';

这只能匹配"张"加任意一个字的人,比如"张三",但匹配不了"张小明"。这个区别在写精确度的需求时很有用。

平时开发时要注意:LIKE如果写成'%关键词%',因为前置通配符的存在,索引通常就失效了。数据量小的表无所谓,数据量大了以后,这类查询会全表扫描,性能直线下降。该用全文索引或搜索引擎的场景,就得及时升级方案,别在LIKE一棵树上吊死。

3.6 空值判断:IS NULL与IS NOT NULL

空值是初学者最容易翻车的地方。NULL在SQL里表示"未知""不存在",它不是0,也不是空字符串。判断空值不能用= NULL,因为前面说过,跟NULL比较得到的结果是UNKNOWN,WHERE永远不会把它当条件成立。

正确的写法是:

SELECT * FROM users WHERE city IS NULL; SELECT * FROM users WHERE city IS NOT NULL;

拿我们的示例数据来说,孙七的城市是NULL,想单独找出一个没有城市信息的用户,必须用IS NULL。如果你用WHERE city = '',那查出来的只会是空字符串的字段,查不到NULL的。这两种情况在实际业务里含义不同:空字符串代表"明确填了空",NULL代表"压根没填或者不知道"。做统计时特别要分辨清楚,否则报表数据对不上,排查半天可能就卡在这里。

4. ORDER BY排序:让结果按你的规则排列

查询结果默认返回的顺序,取决于MySQL内部怎么存储和访问数据,你是不好保证的。想让结果整齐有序,必须主动用ORDER BY。

4.1 升序与降序:ASC和DESC的正确用法

按年龄从小到大的顺序列出用户:

SELECT name, age FROM users ORDER BY age ASC;

按年龄从大到小:

SELECT name, age FROM users ORDER BY age DESC;

ASC是升序(默认),DESC是降序。不写关键字时,默认按升序排。这里有个细节:数字排序好理解,字符串排序也按字典顺序排,但中文字符排序就不是靠直觉了。

中文按的是字符集的编码顺序排序,很可能跟你想要的"按拼音排"不一样。想按拼音排,需要使用CONVERT或指定排序规则(Collation),这不是基础篇的内容,但你可以先有个印象:中文排序会是个潜在的天坑。

4.2 多字段排序:按什么优先,谁在前谁说了算

需求升级一下:先按城市排,同一个城市里的人再按年龄排。写法是把多个字段用逗号分开:

SELECT name, city, age FROM users ORDER BY city ASC, age DESC;

执行的顺序是:先按city排,如果city相同,再按age排。注意,DESC只作用于紧挨着它的那个字段,city不受影响。初学者容易写成:

ORDER BY city, age DESC;

以为两个字段都是降序,其实只有age是降序。如果确实都需要降序,得写成:

ORDER BY city DESC, age DESC;

这个细节在写分页查询或者排行榜类需求时特别重要,排错一个字段,整个榜单数据就全乱了。

4.3 排序与LIMIT配合:取前N条记录

查询里特别常见的需求是"取前几条"。比如找出年龄最大的三个用户:

SELECT name, age FROM users ORDER BY age DESC LIMIT 3;

LIMIT 3表示只返回前3行。它跟在ORDER BY后面时,逻辑顺序是:先按规则把全部数据排好序,再取前3行。顺序反了会让你拿到"排序前的前3行",结果完全不同。

LIMIT也支持偏移量的写法:

SELECT name, age FROM users ORDER BY age DESC LIMIT 3, 5;

这里的3, 5表示从第4行开始取5行(偏移量从0开始),这正是分页的核心逻辑之一。分页参数多了以后,LIMIT的效率问题就会浮现,大表分页要靠"书签"或索引来优化,但这些都是进阶话题了。

4.4 排序时NULL值的处理

NULL在排序中默认认为是最小值,所以升序时排在最前面,降序时排在最后面。比如:

SELECT name, city FROM users ORDER BY city ASC;

结果里城市为NULL的孙七会排在最前面。这个默认行为在不同数据库产品里并不一致,Oracle是把NULL视为最大值,这就容易造成迁移时行为差异。如果你明确想把NULL排到后面,MySQL里可以这样写:

SELECT name, city FROM users ORDER BY city IS NULL ASC, city ASC;

这个写法把city IS NULL当成一个布尔表达式,0排前面、1排后面,先把非空值排出来,再排空值。虽然基础篇不做强制要求,但提前知道这个技巧,将来遇到空值排序疑惑时会省很多时间。

5. 常见问题与排查技巧实录

写查询写了这么多年,几乎每天都会在社区和群里看到新手问类似的问题。我把高频问题整理成一个速查表,你可以存下来当备查手册。

5.1 命令执行不了的常见原因

现象常见原因处理方法
输入SQL后没反应,光标停着不动上一句语句末尾没加分号;打上分号后回车,或输入\c取消当前输入
报错 Table 'xxx' doesn't exist没执行USE或表名拼错确认库名和表名,大小写注意区分
报错 Unknown column列名写错或没加反引号用DESC users;查看表结构确认列名
中文乱码客户端字符集与服务端不一致执行SET NAMES utf8mb4;再查询
一直卡在"Waiting for table metadata lock"有其他会话在操作表结构查看SHOW PROCESSLIST;,找到阻塞的会话处理掉

5.2 引号、分号与关键字的细节

分号的问题值得单独说一遍。在MySQL命令行里,一条语句是以分号结束并执行的。新手最容易犯的错是写完SQL后忘了分号就按回车,结果MySQL认为语句还没结束,光标乖乖跳到下一行等你继续。这时候不要慌,补个分号回车即可,或者用\c退出输入状态。

还有关键字大小写的问题:SELECT、select、Select都能执行,MySQL关键字不区分大小写,但表名和库名在Linux系统上区分大小写,在Windows上不区分。这会导致一个经典问题:代码在本地Windows跑得好好的,一上Linux服务器就报表不存在。要彻底避免,最好统一约定:建表时所有表名、字段名都用小写,写SQL也保持一致。

引号的细节再强调一点:字符串值是单引号,双引号在某些模式下也能用,但不稳定。MySQL默认开启了ANSI_QUOTES的话,双引号会被当成列名引用符,而不是字符串。所以最稳妥的做法是:字符串一律单引号。

5.3 查询性能的入门意识

基础查询的语法都学会了之后,我强烈建议你养成一个习惯:每次写查询,用EXPLAIN看一眼执行计划。

EXPLAIN SELECT * FROM users WHERE age > 25;

这个命令不会真的返回数据,它返回的是MySQL打算怎么执行这条查询的"作战计划"。比如是否全表扫描(type列是ALL),是否用到索引(key列)。从基础阶段就养成看执行计划的习惯,比事后看各种性能优化文章要有效得多。

初学阶段,只需要关注两点:一是type不要是ALL(除非表实在很小),二是key有没有实际用到索引。等这两点有了直觉,你已经比大多数"能用但不懂为什么慢"的开发者强了。

5.4 练习建议与下一步方向

基础查询能覆盖日常开发中很大一部分需求,但它毕竟只是SQL世界的入口。我给你的练习建议是:拿自己手边的任意一张表(比如前面建立的users),把今天这篇里所有示例语句逐条敲一遍,然后改动条件做"如果改成X会怎样"的实验。敲键盘是学SQL性价比最高的方式,看一遍永远比不过敲一遍。

另外提醒一下,学好基础查询后,接下来的自然延伸是GROUP BY分组统计、JOIN多表连接和子查询。这些都是建立在这个基础之上的进阶功能,但地基如果没打牢,后面每走一步都会觉得哪里不对劲。

如果哪天你被某条查询折磨得抓狂,先别急着怀疑自己。把SQL拆开:先看查询的是哪张表,再看过滤条件是否合理,再看排序和分页是否符合预期。九成的问题,出在这几个环节里。记住这个排查顺序,你能比大多数人更快定位到问题所在。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询