你以为你会写 SELECT其实你大概率只是在需要数据的时候复制粘贴一条 SELECT *。这个感受在我重新刷 SQLZoo 的 SELECT 基础练习时特别明显。作为一个平时经常和 MySQL 打交道的人我一直觉得自己写 SQL 没什么问题可真到 SQLZoo 上一题一题过的时候才发现光是 SELECT 基础就能拆出那么多平时压根不会注意的细节。下面这份练习记录就是我完整刷一遍 SQLZoo 上 MySQL select 基础的过程为什么选这个平台、练习题怎么拆、哪些地方反复出错、以及刷完之后怎么在本地 MySQL 环境里重新验证一遍。适合刚学 SQL、想打牢 select 基础的读者也适合那些嘴上说会 select但一写复杂条件就翻车的人。1. 为什么拿 SQLZoo 练 SELECT我的选型思路1.1 浏览器即开即用数据真实有趣先聊一个问题为什么我推荐拿 SQLZoo 练手而不是一上来就装 MySQL。很多新手学 SQL 的第一个门槛根本不是语法而是环境搭建。按教程去下载 MySQL、配置服务、设置密码中间任何一个环节出问题一晚上就没了。SQLZoo 最大的优势是零安装打开官网就能写 SQL它内置了两张表一张是 world 表记录全球国家的名称、大洲、面积、人口、GDP另一张是 nobel 表记录历年的诺贝尔奖数据。这两张表不是无意义的 student、teacher 演示数据而是真实世界的公开数据查起来有代入感。这种即开即用对刷题特别重要。学习 SQL 的核心是手要勤而不是等所有环境都完美配置好再开始。你只需要写一句 select点运行系统就会执行并展示结果。对了就进入下一题错了会看到错误信息。这种即时反馈是保持学习动力最关键的东西。我自己在 SQLZoo 上从 select basics 开始刷十分钟就能进入状态因为每一题都有明确目标不会像对着空数据库那样不知道该干嘛。1.2 和看视频、自己建表相比它赢在反馈市面上学 SQL 的路子无非这么几条看视频教程、买书、自己建表练习、直接在线上刷题平台练。我整理一个表格对比学习方式优点缺点看视频/看书系统性强能听懂概念听懂了和会写是两回事容易眼高手低自己建表练习贴近实际业务自由度高需要自己设计场景和题目大部分人坚持不下来直接上 SQLZoo有现成数据和题目从易到难一键判题题目场景偏基础和复杂业务还有距离只看面试题目标明确贴近求职缺乏系统训练容易被题目吓退比较下来你会发现SQLZoo 适合作为第一遍刷语法的地方。它把 SELECT 基础拆成了很小的关卡每一关只做一件小事。比如先让你查一个国家的面积再让你同时查好几个国家先让你用等于号精确匹配再让你用 LIKE 做模糊匹配。这种循序渐进的设计在心理上很友好不会一上来就甩给你十几张表让你 JOIN。还有一点很关键它自带答案比较。自己建表练的时候你写了 SQL 都不知道对不对还得自己去数结果在 SQLZoo 上错了系统会显示 Expected result 和 Your result 的对比排错过程本身就是学习过程。很多人在这一步养成了先自己读结果再看哪里不对的习惯这个习惯比背语法值钱得多。1.3 SELECT 基础为什么值得单独刷一遍有人觉得select * from table 早就会了刷基础是不是浪费。我的看法是SELECT 是整个 SQL 的地基后面的 JOIN、子查询、GROUP BY 都是在这块地基上盖起来的。你 JOIN 写不明白很多时候不是 JOIN 本身难而是过滤条件和表关系没想清楚你子查询看不懂很多时候是 SELECT 的作用范围没搞明白。刷 SELECT 基础表面上是练语法实际上是在练用条件表达需求的能力。SQLZoo 的基础题做下来覆盖了列选择、WHERE 过滤、IN、BETWEEN、LIKE、DISTINCT、ORDER BY、LIMIT、别名这些点。单独看都很简单但组合起来最容易出问题。所以这一遍基础我当作给自己做一次语法体检哪些知识点以为自己会其实根本经不起问。2. SQLZoo 的 SELECT 基础关卡题目和语法一起拆2.1 最简单的查询选列 条件过滤SQLZoo 的 SELECT basics 模块上来最经典的一题是查出法国的人口SELECT population FROM world WHERE name France这题看起来简单但它包含了一个最核心的理解SELECT 后面的列是你想看到的字段WHERE 后面的条件是决定哪些行满足要求。WHERE 就是过滤器它一行一行扫描条件为真的留下为假的离开。很多新手会困惑为什么是 name France而不是直接查 population其实就是没把选列和过滤行两个动作分开。紧接着它会出一个 IN 条件的题显示瑞典、挪威和丹麦三个国家的名称和人口SELECT name, population FROM world WHERE name IN (Sweden, Norway, Denmark)IN 解决的是多个候选值的需求。你当然可以用 OR 串联写起来会长很多可读性也差。这道题的另一个意义是让你明白SELECT 后面可以放多个列列的顺序就是结果表的列顺序。我建议做题时养成习惯第一眼先问自己我要看哪些列再问条件是啥先后顺序对了SQL 就成功了一半。2.2 进阶条件IN、BETWEEN、LIKE 的实际用法再往下SQLZoo 会让你用 BETWEEN 找面积在 20 万到 25 万平方公里之间的国家SELECT name, area FROM world WHERE area BETWEEN 200000 AND 250000BETWEEN 是闭区间包含两端边界值。也就是说面积等于 200000 和等于 250000 的国家都会被查出来。这一点和有些编程语言里的 range 左闭右开不一样写的时候要特别留意。BETWEEN 适合连续数值区间如果查的是离散的几个值用 IN 更合适。LIKE 和通配符也是重点。题目会让你找出名称以 Al 开头的国家SELECT name FROM world WHERE name LIKE Al%这里 % 表示任意长度的任意字符所以 Al% 能匹配 Albania、Algeria 这类名字。如果把 % 放在两边写成 %United%就能匹配名称任意位置包含 United 的国家比如 United States、United Kingdom。还有一个下划线通配符它只匹配一个字符比如 C 能匹配以 C 开头、后面只有一个字符的字符串。LIKE 是最朴素的模糊搜索实现方式把通配符逻辑吃透后面学正则表达式也不吃力。2.3 控制结果形态DISTINCT、ORDER BY、LIMIT 与别名SELECT 基础练到中间开始涉及结果形态的控制。比如要你用 DISTINCT 查看 world 表里有哪些大洲SELECT DISTINCT continent FROM worldDISTINCT 的作用是去重。很多人刚开始会忽略它直接 select continent from world结果返回几百行里面重复非常多。这个习惯如果带到真实报表里会出现大量看起来一样的行review 的时候又费眼又容易出错。ORDER BY 排序是更常见的需求比如按人口倒序取出前几名的国家SELECT name, population FROM world ORDER BY population DESC LIMIT 5DESC 表示从大到小ASC 是默认从小到大。LIMIT 5 表示只返回前 5 行。这三者配合起来的意图很清晰先对结果集排序再截取前 N 行。需要注意执行顺序是 FROM 先确定表WHERE 再过滤SELECT 选列ORDER BY 排序LIMIT 最后截断。这个顺序概念在后面的分页查询里会反复用到。还有一个容易被忽略但很实用的别名语法SELECT name AS country, population AS people FROM worldAS 是给列起别名别名会直接显示在结果表头上。实际工作中写报表、做接口返回字段映射时别名几乎是必须的。理解别名也是后面子查询和 JOIN 的基础因为多表查询时列名容易冲突靠别名区分是最常见的手段。2.4 澄清一个概念select 不是函数是查询语句搜select函数的时候能看到很多人把它当成某种函数来理解其实 SELECT 在 MySQL 里不是函数而是一条查询语句的开头。真正有函数性质的是 COUNT、SUM、MAX 这类聚合函数或者 DATE_FORMAT 这类格式化函数。在 SQLZoo 的题目页面上你输入的重点不是调用某个 select 函数而是把一条完整的 select 语句组织好选哪些列、从哪张表、用什么条件过滤。理解这一点能帮你少走很多弯路尤其是看资料的时候不会被select 函数这类说法带偏。3. 练习中踩过的坑从答案错误到彻底理解3.1 多条件没加括号逻辑跑偏到意想不到的地方我第一次刷到需要同时使用 AND、OR 的组合条件时写出来的语句是这样的SELECT name, population FROM world WHERE name France OR name Germany AND population 80000000本意是法国或者德国且人口大于 8000 万但实际执行的逻辑却是WHERE name France OR (name Germany AND population 80000000)因为 SQL 里 AND 的优先级高于 OR。如果目标只是法国或德国并且两者人口都大于 8000 万应该这样写SELECT name, population FROM world WHERE (name France OR name Germany) AND population 80000000这个坑在真实业务里极其常见。比如查VIP 用户或最近 30 天有购买记录的用户且状态正常如果 OR 前后不加括号查询范围会比预期大很多统计口径直接出错。我现在的习惯是只要 WHERE 里同时出现 AND 和 OR一律给每个逻辑块加括号不靠记忆赌运算规则。3.2 中文引号和大小写让答案怎么都过不了另一个很阴间的错误字符串条件里的引号打成了中文引号。select * from world where name France如果在全角输入法状态下打引号服务端直接报语法错误而且错误提示往往不直观你可能根本想不到是引号问题。排查办法很简单把整条 SQL 删掉重打或者用有高亮的编辑器看一眼字符串有没有被正确识别。大小写也一样。SQL 关键字本身不区分大小写select 和 SELECT 都能执行但字符串的值是区分大小写的取决于数据库排序规则。在 SQLZoo 的默认环境下France 写成 france大概率查不到结果。数据表里明明有 France但你就是查不出来这时候先怀疑大小写再怀疑引号。补充一个经验SQLZoo 上报错时先把 SQL 复制到本地文本编辑器里看一眼引号、空格再粘贴回题目重试。有时候看起来一模一样的字符串其实混进了全角空格或换行符肉眼看不到删掉重打一遍就好。这类问题多遇几次你反而会对字符敏感以后写 SQL 会更小心。3.3 WHERE 里用列别名以及别名到底能用在哪的问题如果你先学了 2.3 的别名语法很容易顺手写出这种语句SELECT name, population AS p FROM world WHERE p 50000000结果会报错大致意思是 p 这一列不存在。原因就是 SQL 的执行顺序先 FROM 确定表再 WHERE 过滤行最后 SELECT 投影列。WHERE 阶段别名 p 还没生成自然没法引用。正确做法是直接写原始列名SELECT name, population FROM world WHERE population 50000000再补充一个很多人搞混的点WHERE 里不能用别名但 ORDER BY 里可以用这是 MySQL 的实际行为。比如 select name, population as p from world order by p desc 可以正常执行因为 ORDER BY 发生在 SELECT 之后此时别名已经生成。不同数据库在这个细节上不完全一致最稳妥的做法还是统一用原始列名能少踩很多兼容性坑。3.4 分号、空格和字段名拼写最不起眼也最坑SQLZoo 判题对分号还算宽容很多题不写分号也能过。但如果你把同样的 SQL 拿到 MySQL 命令行执行分号就变成语句结束的标志不写它命令行会认为句子没写完。这个差异很影响体验。我的习惯是无论在哪写完一条完整 SQL 都加分号保持肌肉记忆一致省得来回切换。还有格式问题SQLZoo 上完整一行写查询也行但可读性太差。我一般按关键字换行SELECT 一行、FROM 一行、WHERE 条件一行。判题只看逻辑结果不看行格式所以排版不影响正确性。但养成良好排版习惯遇到几百行的大查询你才知道它有多救命。表名和列名拼写错误也会让你收获一个红色错误页。world 表里是 name不是 names是 population不是 populaton。这类问题没有太多捷径唯一建议是做题前先扫一眼页面上给出的表结构字段列表SQLZoo 每个页面都贴了表结构把字段名看清楚再下笔能省掉一半报错。4. 从 SQLZoo 到真实 MySQL环境差异与落地验证4.1 装一个本地 MySQL把练习题亲手跑一遍刷完在线题后我强烈建议装一个本地 MySQL把练过的题重跑一遍。原因很简单SQLZoo 帮你屏蔽了环境问题但真实工作中你总得连数据库、跑脚本早一点接触客户端环境少一些临场慌张。本地安装其实不难。到 MySQL 官网找 community server 版本Windows 下安装向导会引导你设置 root 密码Linux 下可以用系统包管理器安装装完执行 mysql_secure_installation 做基本初始化。装好后命令行执行 mysql -u root -p 能进去就算成功了。接着可以建一个练习库把 SQLZoo 里那两张表简化一下建出来CREATE DATABASE sqlzoo_practice; USE sqlzoo_practice; CREATE TABLE world ( name VARCHAR(100), continent VARCHAR(50), area INT, population BIGINT, gdp DECIMAL(20,2) ); INSERT INTO world (name, continent, area, population, gdp) VALUES (France, Europe, 640679, 67000000, 2715518.00);有了本地环境你再跑一遍刚才那些 select 语句会明显感受到和在线平台的区别查询结果出来得更直接你能看到真实客户端如何处理结果集也可以顺手试一下不写分号、大小写混用会怎样。这种折腾本身就是学习。4.2 SQLZoo 判题和 MySQL 客户端在细节上的差异几个典型差异值得记下来。第一是分号。SQLZoo 多数情况不强制加分号但 MySQL 命令行不加分号就不执行必须用分号结尾再回车。第二是字符串引号。标准 SQL 习惯用单引号SQLZoo 也是这个约定。MySQL 默认同时支持单引号和双引号但在某些模式下双引号会被当成标识符引号所以为了稳妥统一用单引号。第三是标识符引号。MySQL 里如果表名或字段名和保留字冲突需要用反引号括起来比如 selectselectfrom t 这种极端情况。SQLZoo 的题目基本不会遇到但本地建表时要特别注意别用 order、group 这类保留字当字段名。第四是 LIMIT。SQLZoo 有些题目不希望用 LIMIT它更希望你写通用 SQL但真实 MySQL 里 LIMIT 是分页标配不仅能用还非常频繁。刷题时如果某个方案被判错先看看是不是用了平台限制的写法再去查语法本身的问题。4.3 数据类型决定 select 出来后长什么样刷题刷的是逻辑真落地时字段的数据类型会直接影响 select 看到的内容。比如 datetime 类型在 MySQL 里默认显示成 2024-06-01 14:30:00但如果业务只需要日期就要在 select 里做格式化SELECT name, DATE_FORMAT(created_at, %Y-%m-%d) AS created_date FROM user_info数值和 NULL 的组合也很常见。如果 select 出来的列里有 NULL结果集里显示的是 NULL 而不是 0做统计对比时容易看错。处理 NULL 一般用 COALESCE 或 IFNULL 转成默认值。SQLZoo 的基础题里也有一类专门练 IS NULL 和 IS NOT NULL因为 NULL 不能直接用 或 ! 比较。碰到这类题不要跳过它会帮你处理真实数据库里的各种空值情况。5. 刷完 SELECT 基础之后进阶路线与面试衔接5.1 聚合函数与 GROUP BY从查出来到算出来SELECT 基础练完下一步通常是聚合。SQLZoo 的 SUM and COUNT 模块就是把基础 select 和聚合计算接起来的转折点。你会开始写这样的语句SELECT continent, COUNT(*) AS country_count FROM world GROUP BY continentCOUNT、SUM、AVG、MAX、MIN 这些聚合函数配合 GROUP BY能完成按某个维度统计的核心需求。这里最需要留意的还是执行顺序先分组再聚合再过滤。如果要对分组后的结果过滤用 HAVING 而不是 WHERE这一点几乎每次面试都会考。比如只显示国家数超过 20 的大洲SELECT continent, COUNT(*) AS country_count FROM world GROUP BY continent HAVING COUNT(*) 20这一步完成你就从会查表变成了会算数可以开始处理真实的数据分析需求。5.2 子查询和 JOIN基础再往上一层SQLZoo 里的 SELECT in SELECT 模块讲的就是子查询一层 select 套在另一层 select 的条件里。子查询的基础理解是把内层 select 的结果当作外层条件的判断依据。比如SELECT name FROM world WHERE population (SELECT population FROM world WHERE name France)这条语句能查出所有人口比法国多的国家。内层先算出法国人口外层拿这个值去比较。如果基础阶段没弄明白SELECT 是怎么一层层组织起来的到这里会非常懵。JOIN 则是把多张表拼接起来。SQLZoo 的 JOIN 模块用 game 和 goal 表演示比赛与进球关系很适合理解 LEFT JOIN、INNER JOIN 的区别。这两块内容排在 SELECT 基础后面是有原因的——它们都建立在先会选列、会过滤行、会给别名的基础上。基础没打牢到 JOIN 就会卡住。5.3 用面试题反向检验 SELECT 掌握程度如果你在准备面试搜mysql 面试题看到的高频题就是很好的自测清单。面试不会只让你背语法而是给一个场景题比如查出每个部门工资最高的员工查出连续登录 3 天的用户或者查出某张表里重复的数据。这些题表面看着高级拆到底层都离不开 SELECT 基础里的列选择、条件过滤、去重、排序和分组。与其一上来就背高级写法不如先把 SELECT 基础练到条件反射的程度。SQLZoo 的基础题不用全部刷完重要的是在做题过程中形成表结构意识和查询意图拆解习惯拿到一个需求先在脑子里过一遍要查哪张表、用什么条件、最后要看哪些列再落笔写 SQL。这个过程练熟了面试写题和日常开发都会顺手很多。最后分享一点个人体会。我刷完 SQLZoo 的 SELECT 基础再回看日常工作最大的变化不是记住了多少语法而是对查询条件到底在过滤什么变得更敏感。以前写 SQL 全凭感觉报错就去搜现在看到一条复杂查询会先按执行顺序在脑子里过一遍FROM 选了哪张表WHERE 干了什么GROUP BY 怎么分组SELECT 里哪些字段合法ORDER BY 排的是什么LIMIT 最后切了多少行。这套思考方式是在刷题时练出来的比记住一条具体语法值钱得多。如果你也在学 MySQL建议别贪快把 SQLZoo 的基础 select 题认认真真刷完最好是每一题都亲手写一遍不要复制别人的答案。遇到报错先自己看提示找原因实在不行再看讨论区这种靠自己去排查、理解和修正的过程才是练习记录里最宝贵的部分。