先说说我的立场:SQL 从来不是一门“背下来就完事”的语法课,它是一个拿来解决问题的工具。我见过不少朋友抱着《SQL必知必会》啃了一个月,SELECT、JOIN、GROUP BY 都认识,真到写报表、排查慢查询、处理一堆重复数据的时候却完全不知道从哪下手。也有人一上来就扎进 SQL Server 2019、2022 的安装教程里,结果卡在环境那一关,连数据库都没连上就放弃了。
这篇文章我想换个思路,不谈“三十天精通 SQL”这种虚话,而是围绕 SQL 学习这件事本身,把从环境搭建、基础查询、窗口函数,到慢 SQL 优化、SQL 注入、面试和工作场景里那些高频踩坑点串起来讲一遍。内容偏向实践,适合刚入门想建立体系的学习者,也适合已经写过不少 SQL 但发现自己只会“查出来”却不会“查得好”的开发者参考。我能保证的是,里面提到的每一条都是我自己或身边同事真实趟过的经验,不是从文档里复制出来的。
1. 学习 SQL 前,先想清楚你要解决什么问题
1.1 你属于哪一类 SQL 学习者
同样是学 SQL,不同角色的侧重点完全不同。数据分析师日常写的最多是 SELECT、聚合、窗口函数,核心诉求是“把数据取出来并算清楚”;后端开发除了增删改查,还得关注 SQL 性能、事务、锁,因为接口响应慢了要找数据库的问题;DBA 或者运维更关心安装部署、备份恢复、权限管理;还有一批人是准备面试,目标是在刷 SQL 面试题时能快速写对排名、分组 Top N、连续登录这类题型。
我的建议是:开始动手前先明确自己属于哪一类。因为学习路径如果选错,效率会差很多。比如你明明是做数据分析的,却花两周研究 Windows 上怎么装 SQL Server 2008 R2 并打补丁,这就属于方向性浪费。反过来,如果目标是后端开发,只练窗口函数不学索引和 EXPLAIN,后面写接口迟早会被慢查询教做人。
1.2 数据库选型:学哪一套更好
另一个绕不开的问题是:学 SQL 到底选哪个数据库?MySQL、SQL Server、Oracle、PostgreSQL 语法大体相通,但细节差异多得能写一本书。从微信搜出来的热搜词看,SQL Server 相关的安装教程、下载、报错占了相当大的比例,很多人就是被 SQL Server 的环境问题绊住了。
我的个人意见是:第一门数据库选 MySQL 或者 SQL Server 都可以,关键是不要中途频繁切换。SQL Server 在 Windows 环境下图形化管理工具 SSMS 做得非常友好,适合新手直观理解表、索引、执行计划;MySQL 则轻量,跨平台,互联网公司用得更多,后面转其他数据库几乎无压力。更重要的是理解表、主键、外键、索引、事务这些概念,因为它们在所有数据库里是相通的。等你把一套学扎实了,换到另一套只需要花一两天补齐差异点。
1.3 一份可执行的 SQL 学习路线
根据这十几年的经验,我给大多数人的建议是六步走:第一步,掌握 SELECT 基础、WHERE 过滤、ORDER BY 排序;第二步,学会多表 JOIN 和子查询;第三步,吃透 GROUP BY 聚合和 HAVING 过滤;第四步,掌握窗口函数(这是 SQL 能力的分水岭);第五步,学 EXPLAIN 和索引,理解慢 SQL 优化;第六步,了解 SQL 注入和基本的防护手段。
这六步对应的正好是《SQL必知必会》这类书的主要章节,但书只是辅助,真正有效的做法是每学一个语法就在本地数据库里反复验证,拿真实业务数据练手。比如你学会了 COUNT、SUM、GROUP BY,那就自己造一张订单表,算一下每个月的销售额、每个用户的消费次数,练到不假思索为止。语法是死的,数据是活的,只有把语感和业务场景结合起来,SQL 水平才能真正上去。
2. 环境搭建:SQL Server 安装与那些绕不开的坑
2.1 快速装好 SQL Server 2022 并完成连接
环境搭建是很多人学 SQL 的第一道坎,尤其是 SQL Server。我身边至少有五个人因为安装问题劝退了。这里我先给出一条经过验证的 SQL Server 2022 安装路径:先从官方渠道下载 SQL Server 2022 Developer 版(开发版免费,功能完整,学习完全够用),双击运行后选择“基本”安装类型,后续一直点下一步,到“数据库引擎配置”时把身份验证模式改成“混合模式”,设置一个自己记得住的 SA 密码,再指定一个当前 Windows 用户为 SQL Server 管理员。
安装完成后,打开 SQL Server Management Studio(简称 SSMS),服务器名称填本机实例名,比如localhost或.\SQLEXPRESS,身份验证选“Windows 身份验证”就能直接登录。如果要用账号密码登录,就选“SQL Server 身份验证”,输入sa和你设置的密码。这里我特别提醒一句:很多人连不上数据库,并不是安装失败,而是安装时忘了启用 TCP/IP 协议,或者 Windows 防火墙把 1433 端口拦了。你可以在“SQL Server 配置管理器”里检查 SQL Server 网络配置,把 TCP/IP 启用,再在防火墙里放行 1433 端口,这个问题就解决了。
2.2 热搜里那几个报错,其实都好解决
我在这次整理热搜词时看到几个非常典型的 SQL Server 报错,几乎每个都能对应到一段真实踩坑史。比如“警告 26003:无法卸载 Microsoft SQL Server 2008 R2 安装程序支持文件”,这种通常发生在你安装了更高版本 SQL Server 之后又想卸载旧版的时候,安装程序的支持文件版本冲突导致卸载程序不能正常移除组件。遇到这种情况,先别慌,去“控制面板-程序和功能”里找到“Microsoft SQL Server 2008 R2 安装程序支持文件”,手动卸载,如果卸载不了,就用微软官方的“安装媒体清理工具”把旧安装介质清理掉,再尝试安装新版。
另一个高频报错是“无法启动 Windows Management Instrumentation (WMI) 服务”。这个报错和 SQL Server 没有直接关系,而是 Windows 系统服务本身没起来,SQL Server 安装程序在检查环境时发现 WMI 服务不可用,就拒绝了安装。处理思路是先按Win+R输入services.msc,找到 Windows Management Instrumentation 服务,右键启动,并把启动类型设为“自动”;如果启动失败,通常是 WMI 存储库损坏,需要在管理员命令行里执行winmgmt /verifyrepository检查,再执行winmgmt /salvagerepository修复。
还有一个困扰了很多人的连接报错:[08001] [Microsoft][ODBC Driver 18 for SQL Server]命名管道提供程序: 无法打开。这个报错我排查过一次,最终定位是客户端连接字符串里写了服务器名,但服务器上的“命名管道”协议没有启用,或者客户端和服务器版本不匹配。最简单的解决办法是:连接字符串里不写协议前缀,直接改用 TCP/IP,也就是把服务器名写成tcp:localhost,1433,或者在 SQL Server 配置管理器里把 Named Pipes 和 TCP/IP 都启用。ODBC Driver 18 默认要求加密连接,如果不能建立加密连接就会直接失败,在连接字符串里加上Encrypt=No;TrustServerCertificate=Yes也能绕开这个报错。
2.3 不想折腾安装?这些替代方案同样能练
如果你被环境问题折腾得心力交瘁,其实还有更轻量的选择。我有一段时间给团队做分享,统一推荐大家用 Docker 跑 SQL Server 或 MySQL,因为容器化部署只需要一条命令,几分钟就能得到一个干净的数据库环境,随时可以销毁重建。比如想跑 SQL Server 2019,一条docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=YourStrongPassword" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest就搞定了。如果懂一点云服务,也可以直接申请一个云数据库实例,省去本地环境维护的麻烦,还能顺便练练远程连接。
但是我也得说实话:不管用哪种方式,SQL 语法是一样的,环境差异不会影响你做练习。问题的关键不是选哪个数据库,而是你肯不肯花一个下午把环境搞定,然后开始写第一条 SELECT。
3. 基础查询怎么练才有效:必知必会、去重、空值和时间函数
3.1 别用可视化工具逃避手写 SQL
现在很多数据库客户端工具提供了图形化查询界面,点几下就能生成 SQL,这种功能对提效是好事,但对初学者来说可能是陷阱。我面试过不少候选人,问他最近一个月写过什么 SQL,他说“都是客户端自动生成的”,这种回答基本等于在说自己不会写 SQL。我的练习建议是:日常能用 SQL 解决的问题就尽量手写,哪怕是一个极其简单的查询,也坚持自己在编辑区敲出来,而不是靠工具点选。
练习的第一步是有一张可以折腾的表。你可以用下面这段 SQL 创建一张订单表,然后自己造点测试数据,后续所有练习都围绕这张表展开:
CREATE TABLE orders ( id INT PRIMARY KEY, customer_name NVARCHAR(50), product_name NVARCHAR(50), amount DECIMAL(10,2), order_date DATETIME ); INSERT INTO orders VALUES (1, N'张三', N'键盘', 199.00, '2025-01-05 10:23:00'), (2, N'李四', N'鼠标', 89.90, '2025-01-05 14:45:00'), (3, N'张三', N'显示器', 1299.00, '2025-01-08 09:00:00'), (4, N'王五', N'键盘', 199.00, '2025-01-10 16:20:00');然后从SELECT * FROM orders开始,逐步加条件、加排序、加聚合,把每一句执行后想清楚结果为什么是这样的。这个过程看似枯燥,但却是最快建立语感的路径。
3.2 去重查询与空值处理:两个让新手翻车的经典场景
热搜词里“sql语句去重查询”和“清洗---sql语句去重”出现频率很高,说明这是真实业务中非常常见的需求。去重有两条路:SELECT DISTINCT和GROUP BY。两者都能去重,但使用场景有点区别。DISTINCT适合对一整行去重,比如查所有客户的去重列表;GROUP BY则更灵活,因为你可以结合聚合函数算出每个分组里的数量、金额、平均值。例如查每个客户的累计消费金额,就必须用 GROUP BY:
SELECT customer_name, SUM(amount) AS total_amount FROM orders GROUP BY customer_name;但注意,GROUP BY查出的是“每个组一行”,如果你同时想看到明细数据,就要用窗口函数或者子查询,这是另一个话题,后文会讲。去重还有一个超高频问题:COUNT(DISTINCT column)在数据量特别大的时候效率会明显下降,所以做全量去重统计时,要先评估数据量级,别一个 SQL 甩过去让数据库硬扛几分钟。
空值处理是另一个新手重灾区。SQL 里的NULL不是空字符串,也不是数字 0,它代表“未知”。用WHERE column = NULL永远查不到数据,必须写成WHERE column IS NULL或者IS NOT NULL。处理空值常用的手段是ISNULL(SQL Server 语法)或COALESCE(标准 SQL,几乎所有数据库都支持),比如把空值替换成 0:SELECT COALESCE(amount, 0) FROM orders。热搜词里还有“sql去除空值”,这通常包括两层含义:一是过滤掉值为 NULL 的行,二是把字段中的空格、制表符清理干净。前者用WHERE条件,后者往往得配合TRIM、REPLACE做数据清洗。这两件事在处理脏数据时基本是标配动作。
3.3 时间函数:SQL Server 与 MySQL 的常见差异
时间处理在 SQL 学习里占比很大,热搜里“sql server 时间函数”也榜上有名。时间函数最大的特点是“方言化”,不同数据库的名字和写法差异很大。SQL Server 常用的是GETDATE()、DATEADD(day, 1, order_date)、DATEDIFF(day, order_date, GETDATE())、YEAR(order_date)、FORMAT(order_date, 'yyyy-MM-dd');MySQL 则对应NOW()、DATE_ADD(order_date, INTERVAL 1 DAY)、DATEDIFF(order_date, NOW())、YEAR(order_date)、DATE_FORMAT(order_date, '%Y-%m-%d')。
我给初学者的建议是:同一个需求至少用两种数据库写一遍,比如“统计最近七天每天的订单金额”,在 SQL Server 里写WHERE order_date >= DATEADD(day, -7, GETDATE()),在 MySQL 里写WHERE order_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)。这样你就能深刻理解“会一门 SQL 不等于会所有 SQL”这个现实,同时也能在面试时显得知识面更广而不是只会背一套语法。
4. SQL 窗口函数:水平进阶的关键一步
4.1 窗口函数解决了什么问题
很多同学写 SQL 碰到“取每个分组里排名前三的记录”“计算累计销售额”“求去年同期对比”这类需求时就卡住了,要么写一堆子查询把 SQL 搞得天翻地覆,要么干脆导到 Excel 里手工算。窗口函数正是为这类场景而生的。它能在不改变行数的情况下,对每一行附加一个聚合值或排名值,相当于在原来的明细结果旁边多开了一列“计算列”,所以也叫“分析函数”。
理解窗口函数,我觉得最好的类比是:普通GROUP BY就像把一堆积木按颜色分进几个盒子里,每个盒子只给你一个汇总数;窗口函数则是给每块积木贴一张便签,上面写着“你属于这个颜色的盒子,你在盒子里排第几”。这个能力在做排名、同比、环比、移动平均时极其有用。
4.2 常用窗口函数速查
窗口函数的核心语法是:函数() OVER (PARTITION BY 分组列 ORDER BY 排序列)。常用的包括:
ROW_NUMBER():给每组内的行按顺序编号,编号不重复。RANK()和DENSE_RANK():排序后排名,区别是并列时是否跳过序号。SUM() / AVG() / COUNT():配合OVER实现累计或移动汇总。LAG()和LEAD():取当前行前一行或后一行的值,常用于环比计算。
拿一个经典的分组 Top N 需求举例。比如要查每个客户购买金额最高的订单,可以这样写:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_name ORDER BY amount DESC) AS rn FROM orders ) t WHERE rn = 1;这里子查询的作用是先给每个客户按金额降序编号,然后外层过滤出每个客户编号为 1 的那一行。注意RANK()和ROW_NUMBER()的区别:如果两个订单金额一样,RANK()会给并列第 1、然后跳到第 3,ROW_NUMBER()则会按顺序硬编号为 1、2。需要哪一种,取决于业务场景。
4.3 窗口函数的高频场景:连续登录、累计值和 Flink SQL
窗口函数的实际应用场景非常多,我举三个最常见的。第一个是“连续登录天数”,这类题在面试中出现的概率极高。思路是先对登录日期去重,再用ROW_NUMBER()给每个用户的登录记录编号,然后用登录日期减去编号得到一个日期,同一个日期说明这些登录记录在时间上是连续的,最后按用户和这个差值日期分组计数。
第二个是“累计销售额”。比如要计算 2025 年每个自然日前一天的累计销售额,用SUM(amount) OVER (PARTITION BY YEAR(order_date) ORDER BY order_date)就能轻松实现。这里要注意ORDER BY后面加不加ROWS BETWEEN会影响窗口范围,默认是对从分组第一行到当前行做累计,这正好满足“截至当前”的需求。
第三个是流式计算场景,热搜里出现了“flink sql中water”,我猜是想问 Flink SQL 里 watermark 和窗口的关系。Flink SQL 中的窗口分为滚动窗口、滑动窗口、会话窗口,watermark 是用来处理乱序事件时间的机制。虽然 Flink SQL 的语法和离线 SQL 略有区别,比如用TUMBLE_START、HOP_START这类函数,但窗口函数的核心思想是完全一致的:先把数据按某种条件切分成“窗口”,再在窗口内做聚合或排名。所以,把离线 SQL 的窗口函数学扎实,转流式 SQL 时会顺畅很多。
5. 慢 SQL 优化:从 EXPLAIN 到并行优化的全链路排查
5.1 慢 SQL 的判定标准与排查入口
在真实业务中,“SQL 能跑出结果”和“SQL 能扛住线上流量”是两码事。慢 SQL 优化是后端开发和 DBA 的核心工作之一,热搜词里“慢sql优化 explain主要看哪些信息”直接问到了点子上。那么哪些 SQL 算慢 SQL?MySQL 默认的执行时间阈值是 10 秒,但这只是默认值,线上业务通常要求单次查询控制在 100 毫秒以内,超过 1 秒的接口就已经明显影响体验了。
排查慢 SQL 的第一步是开启慢查询日志。MySQL 中可以通过SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1;来开启,并设置超过 1 秒的查询记录到日志中。拿到慢查询列表后,先看 SQL 本身:是不是没加条件全表扫描?是不是对索引列做了函数运算导致索引失效?是不是 Join 的顺序有问题?这些都排除后,再上 EXPLAIN 看执行计划。
5.2 EXPLAIN 主要看哪些信息
拿 MySQL 的EXPLAIN SELECT ...举例,输出结果里我最关注的是这几列:type、key、rows、Extra。type是访问类型,从好到差大致是system、const、eq_ref、ref、range、index、ALL,其中ALL表示全表扫描,这是最需要警惕的;key表示实际用到的索引,如果为 NULL,说明没有命中索引;rows是估算扫描行数,这个数字越大,SQL 越危险;Extra里出现Using filesort或Using temporary时,说明排序或去重用了临时文件/临时表,通常意味着还有优化空间。
看执行计划时有一个容易忽略的点:rows只是估算值,不精确,但可以用来做量级判断。比如一个查询的 rows 显示 100 万,即使type显示range,它也可能因为扫描量太大而慢;这时候就该考虑重新设计索引或改写 SQL。另外,EXPLAIN只能看到执行计划,不能直接反映实际耗时,所以线上排查时建议开启 profiling 或者用EXPLAIN ANALYZE(MySQL 8.0+ 支持)拿到真实的执行时间和各阶段耗时。
5.3 索引设计和并行 SQL 优化的取舍
慢 SQL 优化绕不开索引。核心原则是:尽量让 WHERE、JOIN、ORDER BY 用到的列走索引,避免在索引列上做运算,避免使用LIKE '%xx%'这种前置模糊查询。还有一个很重要的概念是联合索引的最左前缀原则:比如建立了(customer_name, order_date)联合索引,那么查询条件里包含customer_name或同时包含customer_name和order_date才能用到索引。这也是面试里常考的知识点。
热搜里的“并行sql优化”指的更多是数据量特别大的场景,比如数仓里的 SQL。并行优化不是让单条 SQL 在多个 CPU 上同时跑那么简单,它涉及到分区裁剪、分桶、资源队列管理等。在传统关系型数据库中,一条大查询如果被拆成多个并行子任务,反而可能因为 I/O 争用、锁冲突而变慢,所以并行度不是越高越好。我见过不少团队一遇到慢 SQL 就盲目加并行度,结果数据库 CPU 被打满,查询反而更慢。正确思路是先确认瓶颈在 CPU 还是 I/O,再用测试环境逐步调参。
6. SQL 注入:安全视角下的攻防与防护
6.1 SQL 注入是怎么发生的
提到 SQL,绝对不能绕过安全话题。热搜里的“sql注入万能密码绕过”和“ctfshow web入门 sql注入”说明很多学习者在接触 CTF 或开发时都意识到了这个问题。SQL 注入的核心原因只有一个:把外部输入直接拼到了 SQL 语句里。比如一个登录功能,后台如果写的是SELECT * FROM users WHERE username = 'admin' AND password = '123456',攻击者提交用户名admin' --,拼接后得到SELECT * FROM users WHERE username = 'admin' --' AND password = '123456',后面的密码校验被注释掉,攻击者就能直接绕过登录。这就是所谓的“万能密码”原理。
CTF 里的 SQL 注入题型变化很多,有字符型、数字型、报错注入、盲注、堆叠注入等。初学者很容易迷进去,把时间花在记住各种绕过姿势上。我的观点是:CTF 是练手的好地方,但重心应该放在“为什么能注入”和“如何从根本上防止注入”这两个问题上。如果只学绕过技巧,那是在研究攻击;真正解决问题,要靠防御。而防御的手段其实非常成熟且不复杂。
6.2 最有效的防护:参数化查询与预编译
防止 SQL 注入最有效、也最通用的手段就是参数化查询,也就是“预编译”。拿 Java 的 JDBC 举例,应该用PreparedStatement而不是拼字符串:
String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, username); pstmt.setString(2, password); ResultSet rs = pstmt.executeQuery();参数化查询为什么能防注入?因为它把 SQL 的结构和数据分开了,数据库在预编译阶段已经确定了 SQL 的语义,后面的输入无论怎么变,都只是被当作字符串值处理,不会再成为 SQL 结构的一部分。ORM 框架比如 MyBatis 里用#{}也会生成预编译语句,但用${}直接拼接就重新掉进了注入的坑。另外,再强调一句:只要接口层做了参数化校验,数据库账号按最小权限分配(比如应用账号不给 DROP、DELETE 权限),注入风险就已经压到很低了。
6.3 正则校验和 WAF 只能作为补充
很多人问我,加了正则校验、上了 Web 应用防火墙(WAF)是不是就安全了?这些只能算补充手段,不能作为防线主体。因为输入校验总有遗漏的边界,WAF 规则也总有绕过的方式,尤其遇到unicode、URL 编码、注释符混用的时候,规则库很容易被绕过。真正的安全必须依赖数据库层面的参数化查询和权限控制,这两道闸门做到位了,才是从根上解决问题。做安全的思路永远是“纵深防御”,不要迷信任何单层防护。
7. 面试与工作场景:SQL 题和工具链的实用心得
7.1 工作里的经典场景:去重、空值、身份证号导出
聊到真实工作,有几个场景我在团队里反复强调,因为几乎每个月都能遇到。第一个是“去重后统计”,比如统计一个活动里的唯一用户数,很多人会写SELECT COUNT(DISTINCT user_id),数据量小没问题,数据量大了以后速度非常慢;改进方案是先尽量缩小 WHERE 范围,再考虑用近似去重函数,或者把去重结果落到中间表,而不是每次实时算。
第二个是“身份证号导出变成科学计数法”问题,热搜里出现了“oracle 数据库sql导出的身份证信息是科学计数法,怎么正确显示身份信息”。这本质上不是 SQL 问题,而是存储和展示格式的问题。身份证号是 18 位,超出了一般数值类型的精度范围,如果字段类型是 NUMBER 或者 Excel 单元格是常规格式,就会变成科学计数法。正确做法是:数据库里用 VARCHAR2 存身份证号,导出时就保持了字符串格式;如果已经是数值型,就用TO_CHAR(id_card)转成字符串。这条经验虽然不起眼,但初入职场的人很容易踩,导致交付出去的报表数据没法用。
7.2 工具链:SQL 转 ER 图、IDEA 插件拼装 SQL
工作中顺手的小工具能省下大量时间。比如有人问“sql转er图”,其实就是把建表语句可视化成实体关系图,方便看表结构、做评审。这类工具有不少,在线版的有 dbdiagram.io,客户端的有 DataGrip 的 Diagram 功能、Navicat 的模型功能,都能把已有数据库表结构反向生成 ER 图。理解表之间的关系,尤其是主外键对业务建模的意义,对写 JOIN 很有帮助。
再比如“idea 插件拼装 sql”,这是开发同学很喜欢的话题。IDEA 里最常用的就是自带 Database 工具,连接数据库后可以直接在编辑器里写 SQL,有语法高亮和补全;一些插件比如 MyBatis Log Plugin,可以把 MyBatis 打印出来的预编译 SQL 还原成可执行的完整 SQL,排查问题的时候非常方便。不过我也提醒一句:工具终究辅助你理解,别过度依赖自动补全,面试的时候可没有插件帮你拼 SQL。
7.3 面试题准备的几个方向
最后聊聊 SQL 面试题。从热搜词可以看出,“sql面试题”是很多人搜索的目标。我面试别人时,Sql 相关考察主要围几个方向:基础查询和聚合、多表关联、窗口函数、去重和空值处理、索引与慢查询优化、SQL 注入防护。这些正好也是日常工作中最高频的能力。
准备面试时不要只背题,要能说清楚“为什么”。比如问到LEFT JOIN和INNER JOIN的区别,不仅要会说结果集大小不同,还要能解释什么时候该用哪种;问到窗口函数,除了会写ROW_NUMBER(),还要能讲清楚RANK()和DENSE_RANK()的区别。把“为什么”想明白了,面试官怎么变着问都能应对。
这套 SQL 学习体系,其实不复杂,核心就是六个字:多写、多想、多查。如果你刚起步,别急着追求最新版本或者全网最详细的安装教程,先把环境搭好,然后找一份真实业务数据,哪怕是自己模拟一份,从每天的简单查询开始坚持练习。我个人这些年看过太多人收藏了一堆教程、下载了一堆资料,最后连一张表都没建出来。真正有用的不是你存了多少内容,而是你亲手执行过多少条 SQL、排查过多少个报错。每次卡住的时候,把报错信息原样复制出来去搜,多踩几个坑,你的 SQL 能力就是在这些坑里长出来的。