Java 实习生入职必看!SQL 从入门到避坑,搞定日常开发没问题
引言:我也曾卡在 “写 SQL” 上
作为秋招上岸的 Java 实习生,刚入职那周我慌得一批 —— 导师扔过来一个 “故障列表查询” 需求,我盯着 Navicat 半天,连 “怎么关联设备表查名称” 都想不明白。
后来翻了无数业务代码、整理了自己踩的坑,才发现实习生日常用的 SQL 根本不用 “炫技”,把基础语法 + 避坑点吃透,80% 的开发需求都能搞定。所以今天把这份 “实习生专属 SQL 笔记” 分享出来,希望能帮到刚入职、或即将入职的你~
一、SQL 基础:从 “会查” 到 “查对”(1 周吃透)
实习生最常做的就是 “查数据”—— 列表页、详情页、简单统计,核心就是SELECT相关语法,重点是 “精准” 和 “高效”。
1. 指定字段查询:别再用SELECT *了!
刚学 SQL 时总觉得SELECT *方便,但实际工作中会踩两个坑:
- 浪费性能:如果表有 20 个字段,你只需要 3 个,查全字段会多传很多无用数据;
- 字段变更报错:比如表新增了一个字段,
SELECT *会把新字段也查出来,若 Java 实体没对应,MyBatis 映射直接报错。
正确示例(查询故障表关键信息,只查需要的字段):
SELECT fault_id, device_id, fault_time, fault_reason
FROM fault
WHERE fault_time >= '2024-01-01' AND device_id = 'DEV001';
2. 去重查询:DISTINCT解决 “重复数据”
场景:查 “所有出过故障的设备 ID”“有哪些故障类型”,避免重复值。
示例(查 1 月份所有出现故障的设备,不重复):
SELECT DISTINCT device_id
FROM fault
WHERE fault_time BETWEEN '2024-01-01' AND '2024-01-31';
3. 条件筛选:WHERE常用运算符
这是 “精准查数据” 的核心,我整理了实习生最常用的运算符,直接套模板就行:
| 运算符 | 用途 | 示例(故障查询场景) |
|---|---|---|
= / != |
等于 / 不等于 | device_id = 'DEV001' |
> / < / >= / <= |
范围比较 | fault_time >= '2024-01-01' |
BETWEEN ... AND ... |
闭区间匹配(含边界) | fault_time BETWEEN '2024-01-01' AND '2024-01-31' |
IN / NOT IN |
多值匹配 | device_id IN ('DEV001', 'DEV002') |
LIKE |
模糊查询(%匹配任意字符) |
fault_reason LIKE '%信号%'(查 “信号” 相关故障) |
IS NULL / IS NOT NULL |
空值判断 | remark IS NOT NULL(查有备注的故障) |
4. 排序与分页:列表页必备
-
排序
ORDER BY:列表页一般要 “最新数据在前”,用DESC(倒序);ID 升序用ASC(默认可省略)。示例(按故障时间倒序,最新故障排第一):sql
SELECT fault_id, fault_time FROM fault ORDER BY fault_time DESC; -
分页
LIMIT:一次性查太多数据会卡死接口,分页是必选项(MySQL 用LIMIT,Oracle 用ROWNUM,实习生先记 MySQL 的就行)。示例(查第二页数据,每页 10 条):sql
SELECT fault_id, fault_time FROM fault ORDER BY fault_time DESC -- 两种写法:LIMIT 偏移量, 条数 / LIMIT 条数 OFFSET 偏移量 LIMIT 10, 10; -- 偏移量=10(跳过前10条),条数=10(取10条)
二、数据操作:增删改的 “安全法则”(1 周熟练)
除了查询,实习生也会做简单的 “增删改”(比如新增故障记录、修改故障状态),核心是 “别搞崩数据”—— 这部分踩坑后果比查询严重多了!
1. 新增数据INSERT:推荐 “指定字段”
两种常用写法,批量新增效率更高,但不管哪种,都要指定字段(避免表字段顺序变了导致数据插错)。
-
单条新增:
INSERT INTO fault (device_id, fault_time, fault_reason, closedloop_state) VALUES ('DEV001', '2024-05-20 14:30:00', '信号中断', '未闭环'); -
批量新增(比循环单条插快 10 倍):
INSERT INTO fault (device_id, fault_time, fault_reason) VALUES ('DEV001', '2024-05-20 14:30:00', '信号中断'), ('DEV002', '2024-05-20 15:00:00', '电源故障');
2. 修改数据UPDATE:必须加WHERE!
重中之重:没加WHERE的UPDATE会全表更新,比如你想改 “F001” 的状态,结果把所有故障的状态都改了 —— 我刚入职时差点这么干,幸好先在 Navicat 测了!
正确示例(修改指定故障 ID 的状态和更新时间):
-- 技巧:先写WHERE,再写SET,防止手抖
UPDATE fault
SET closedloop_state = '已闭环', update_time = '2024-05-21 10:00:00'
WHERE fault_id = 'F001'; -- 精准定位,只改这一条
3. 删除数据DELETE:慎用!加WHERE!
和UPDATE同理,没WHERE会全表删除,而且删了可能恢复不了(除非有备份)。实习生一般只删 “无效数据”(比如过期的测试数据)。
示例(删除 1 个月前已闭环的无效故障):
DELETE FROM fault
WHERE fault_time < '2024-04-20' AND closedloop_state = '已闭环';
三、进阶查询:搞定统计与跨表(2 周掌握)
学会基础后,会遇到 “统计每个设备的故障次数”“查故障时显示设备名称” 这类需求,需要聚合函数+GROUP BY和JOIN。
1. 聚合查询:COUNT/SUM/AVG+GROUP BY
常用聚合函数整理好了,直接套场景用:
| 函数 | 用途 | 示例(故障统计) |
|---|---|---|
COUNT(*) |
统计总行数(含 NULL) | COUNT(*)(查故障总数) |
COUNT(字段) |
统计非 NULL 的字段数 | COUNT(remark)(查有备注的故障数) |
SUM(字段) |
求和(数值型字段) | SUM(fault_duration)(总故障时长) |
AVG(字段) |
求平均值 | AVG(fault_duration)(平均故障时长) |
MAX(字段) |
最大值 | MAX(fault_time)(最新故障时间) |
分组统计示例(查 1 月份故障数 > 5 的设备,按设备 ID 分组):
SELECT device_id, COUNT(*) AS fault_count -- 别名fault_count,方便Java映射
FROM fault
WHERE fault_time >= '2024-01-01'
GROUP BY device_id -- 按设备分组,每组统计一次
HAVING COUNT(*) > 5; -- 筛选分组结果(类似GROUP BY的WHERE)
2. 多表联合查询:INNER JOIN和LEFT JOIN
实习生最常用的两种连接,记住 “用左表还是内表” 的场景就行:
-
内连接
INNER JOIN:只查 “两张表都有匹配数据” 的行(比如查 “有故障记录且设备表存在的设备”)。示例(查故障 + 设备名称,只显示两边都有的数据):SELECT f.fault_id, f.device_id, d.device_name, f.fault_time FROM fault f -- 表别名f,简化写法 INNER JOIN device d -- 关联设备表,别名d ON f.device_id = d.device_id; -- 关联条件:必须加!否则笛卡尔积(数据爆炸) -
左连接
LEFT JOIN:左表(比如 fault)的所有行都显示,右表(比如 device)没匹配的显示NULL(场景:“即使设备表没数据,也要显示故障记录”)。示例(确保所有故障都显示,无设备匹配时 device_name 为 NULL):SELECT f.fault_id, f.device_id, d.device_name FROM fault f LEFT JOIN device d ON f.device_id = d.device_id;
3. 简单子查询:先查 ID,再查详情
场景:“先查未闭环的故障 ID,再查这些故障的详情”—— 不用写复杂关联,子查询更直观。
示例:
SELECT fault_id, device_id, fault_time
FROM fault
WHERE fault_id IN (
-- 子查询:先查未闭环的故障ID
SELECT fault_id
FROM fault
WHERE closedloop_state = '未闭环'
);
四、实习生不用慌:这些复杂 SQL 暂时不用学!
刚入职时我总担心 “不会复杂 SQL 会不会被骂”,后来发现根本没必要 —— 这些内容多是数据分析师、高级后端用的,实习生不用浪费精力:
- 复杂子查询(多层嵌套、关联子查询);
- 窗口函数(
ROW_NUMBER()、RANK(),用于复杂排名); - 存储过程、函数(自定义 SQL 逻辑,维护成本高);
- 事务、锁(框架会处理,不用手动写
BEGIN/COMMIT); - 数据库优化(索引、执行计划,资深开发负责)。
五、避坑指南:比语法更重要的 4 件事!
我踩过的坑,希望你别踩 —— 这些细节比 “会不会写 SQL” 更影响工作效率(甚至避免背锅)。
1. 防 SQL 注入:用#{}代替${}
这是 Java 实习生必须记的!MyBatis 中:
#{}:预编译参数,会把参数当成 “值” 处理,比如传入'1 OR 1=1',会当成字符串,避免注入;${}:直接拼接字符串,比如传入'1 OR 1=1',会变成WHERE id = 1 OR 1=1,查所有数据,风险极高!
正确示例(MyBatis 映射文件):
<!-- 正确:用#{deviceId} -->
<select id="getFaultByDeviceId" parameterType="String" resultType="Fault">
SELECT * FROM fault WHERE device_id = #{deviceId}
</select>
2. 字段别名:适配 Java 驼峰命名
数据库字段一般是下划线(fault_id),Java 实体是驼峰(faultId),如果不写别名,MyBatis 会映射失败(查不到faultId)。
正确示例(用AS取别名):
SELECT fault_id AS faultId, device_id AS deviceId, fault_time AS faultTime
FROM fault;
3. 测试 SQL:先在工具里跑通!
别写完 SQL 就直接贴到代码里 —— 先在 Navicat/DBeaver 中执行,看结果是否符合预期:
- 分页是否正确(比如第二页是不是从第 11 条开始);
- 关联查询有没有数据(比如左连接是不是所有故障都显示);
- 统计结果对不对(比如故障数是不是和手动数的一致)。
我之前没测试就上线,结果分页查出来的是重复数据,接口联调时才发现,浪费了半天时间。
4. 写 UPDATE/DELETE:先写WHERE再写其他!
再强调一次 —— 防止手抖删全表:
- 写
UPDATE时:先写WHERE fault_id = 'F001',再写UPDATE fault SET ...; - 写
DELETE时:先写WHERE ...,再写DELETE FROM ...。
六、我的学习路径:2 周搞定日常 SQL!
最后给个实习生友好的学习计划,按这个来,不用焦虑:
- 第 1 周:熟练
SELECT基础(筛选、排序、分页)+INSERT/UPDATE/DELETE,能独立写列表查询、简单增删改; - 第 2 周:掌握
COUNT/GROUP BY+JOIN+ 简单子查询,能看懂业务代码中的关联查询,自己写统计 SQL; - 实践:结合公司业务练手,比如 “写一个故障列表接口(带时间筛选、分页、关联设备名称)”—— 边用边记,比死记语法快 10 倍。
结尾:SQL 学习不用 “炫技”,正确安全就好!
作为实习生,我们写 SQL 的核心目标是 “正确拿到数据、不搞崩数据”,不用追求 “写出别人看不懂的复杂 SQL”。遇到不会的,先翻公司现有业务代码(复制模板改条件),但一定要先在数据库工具(Navicat/DBeaver)中跑通写好的 SQL 不要直接上线,先在工具中执行,看结果是否符合预期(比如分页是否正确、关联是否有数据),避免接口联调时才发现 SQL 错误。
再不行就问导师 / 同事 —— 大家都经历过 “不会 SQL” 的阶段,没人会嘲笑你~
如果这篇笔记帮到你了,欢迎在评论区交流你的 SQL 学习心得;如果有遗漏的避坑点,也欢迎补充~ 一起做 “不踩坑的 Java 实习生”!
更多推荐
所有评论(0)