ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL零基础入门:SELECT查询与WHERE条件筛选详解

MySQL零基础入门:SELECT查询与WHERE条件筛选详解 干了这么多年数据库带过不少新人发现大家学到查询这块最容易卡壳。前面几篇讲完安装、建库、建表到这儿反而懵了明明SELECT看起来就两个单词怎么一到真项目里就写不对这篇零基础第八篇就是专门把基础查询这层窗户纸捅破的。我会按照实际干活时的思路来讲先搞清楚查询是查什么再一步步拆解SELECT的各个零件。从最简单的全表查询到带条件的精准筛选再到排序最后把我这些年踩过的坑一并端出来。你可以放心这篇不需要任何基础只要前面几篇照着做完、手头有个能跑的MySQL环境就能跟着上手。1. 查询之前先把查什么这件事想明白1.1 连接数据库并确认目标表很多新手一上来就写SELECT结果报错Table doesnt 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_at1张三25北京100.002024-01-052李四30上海200.002024-03-103王五25北京50.002024-06-154赵六28广州0.002024-08-205孙七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离散值的快速匹配如果条件变成城市是北京或上海或广州你可以用一连串ORSELECT * FROM users WHERE city 北京 OR city 上海 OR city 广州;这么写虽然对但很啰嗦还容易漏掉条件。更干净的方式是INSELECT * FROM users WHERE city IN (北京, 上海, 广州);IN后面跟一个值列表只要字段值在这个列表里行就会被选中。反过来想排除这些城市就用NOT INSELECT * 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;也可以用BETWEENSELECT * 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模糊查询%和_的用法模糊查询是基础查询里非常有代表性的需求。比如你想找所有姓张的用户完全相等匹配肯定不适用这时候要用LIKESELECT * 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比较得到的结果是UNKNOWNWHERE永远不会把它当条件成立。正确的写法是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 doesnt 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拆开先看查询的是哪张表再看过滤条件是否合理再看排序和分页是否符合预期。九成的问题出在这几个环节里。记住这个排查顺序你能比大多数人更快定位到问题所在。
返回列表