做软件测试,尤其是功能测试和接口测试的,早晚会遇到一个躲不开的场面:你刚提交了一个bug,开发回复“数据是正常的,你再去库里看看”。这时候你打开数据库管理工具,面对一张表,却连“查出来给我看看”都做不到,空气就会突然安静。所以哪怕每天只花5分钟,SQL语句里的表查询和增加命令,也值得测试人员认真过一遍。
这篇内容就把SQL里最简单的两类命令拆开讲:查(SELECT)和增(INSERT)。不是要你变成数据库专家,而是把测试工作中最高频用到的写法讲透,再配一套每天5分钟的训练安排,让基础薄弱的人几周内就能写对查询、做好数据验证,甚至能自己造测试数据。适合刚入行软件测试、准备面试、或者做了几年功能测试但一直靠“复制别人SQL”过日子的朋友。
1. 测试为什么要先学查询和增加:两条命令撑起半个日常
1.1 查询命令是测试的“眼睛”
功能测试做了半天,页面上的提示都正确,你真敢说这个功能没问题吗?我见过不少新人,注册流程跑通了就报“通过”,结果开发一查库,手机号字段存错了,或者时间少了8个小时。这类问题在页面上根本看不出来,必须靠SELECT语句去表里核实。
注册功能来自是测试人员最常遇到的一类场景:页面提示注册成功,这只是第一步。真正的断言往往要落到数据库——打开用户表,执行一句SELECT,确认username、phone、create_time这些字段是否和测试数据对得上。只有这一步过了,用例才能算真正通过。
所以我一直觉得,查询命令就是测试人员的“眼睛”。学会SELECT,你在项目中就有了独立的判断力,不会开发说什么就信什么,也不会对着一个异常数据干瞪眼。很多线上问题排查到最后,其实就是一句SELECT看数据的事。
1.2 增加命令让你从被动等数据变成主动造数据
测试过程中还有一个高频痛点:没有合适的测试数据。想测“已实名用户才能购买”的流程,翻遍测试环境找不到已实名账号;想测列表分页,表里只有3条数据,根本凑不出第二页。这个时候,如果你只会问开发“能不能帮我插一条数据”,节奏就完全被别人拿捏了。
而INSERT语句就是解决这个问题的钥匙。自己往表里加一条符合前置条件的数据,被测流程立刻就能跑起来。这不是什么“偷懒技巧”,是测试人员该有的基本能力。你在测试环境自己造数据、自己清理数据,本来就是工作的一部分。
说实话,我从功能测试转到自动化测试、再到带测试团队,最明显的感觉是:那些能自己造数、自己查数的人,遇到问题从来不会干等。他们先自己去库里看看,实在看不出来再找开发。这种主动性,几乎就是测试工程师分水岭。
1.3 测试岗位对SQL的真实要求层级
我面试测试候选人时,从来不要求对方能写多复杂的多表关联SQL。但下面这四件事,至少得会三件:
- 能读懂开发写的SQL,排查问题时能理解对方在查什么。
- 能自己从一个表里查出需要的数据,加条件、排序、限量。
- 能自己构造测试数据,用INSERT把前置数据准备好。
- 能验证数据是否正确落库,一般就是把INSERT和SELECT搭配使用。
看清楚,测试岗位要的从来不是“数据库开发”水平,而是“够用且准确”。你不需要背所有函数,不需要懂存储过程优化,但你要能流畅地把“查出来看看”和“造一条数据”这两件事做对。这就是本文标题里“每天5分钟”能成立的原因——学习范围本就是有限的。
2. SELECT 查询命令:测试岗位最常用的几种写法和为什么
2.1 最基础的查询长什么样:SELECT 指定列还是 SELECT *
先说语法骨架:
SELECT 字段1, 字段2 FROM 表名 WHERE 条件;测试同学的新手期,最常写的是SELECT * FROM 表名;,把整张表所有列都拉出来。这个写法用于快速看全貌没问题,但日常验证数据时,我并不推荐你一直用它。
原因很简单:一张业务表动辄几十个字段,SELECT *会把不相关的列全甩在你面前,真正的关键字段反而被埋没。比如你要验证用户注册,关心的就是username、phone、status、create_time四个字段,那就明确写出来:
SELECT username, phone, status, create_time FROM user WHERE username = 'test_001';结果干净、清晰,一眼就能核对。另一个隐藏好处是,当你对着一张列很多的表时,写清字段名能逼自己想清楚“我到底要验证什么”,这对测试用例设计是有帮助的。
还有一个比较冷门但很实用的写法:SELECT 1;。它不查任何表,只用来确认数据库连接是否正常。测试环境切换数据库、排查连接问题时,先跑一句这个,能快速排除连错库的可能性。
2.2 WHERE条件不只是“等于”,测试更常用这几类运算符
WHERE是用来过滤数据的,它解决的场景是:数据太多,我只想精确看某一条或某一类。测试里最常见的条件有这么几类。
精确匹配,用的就是等号和不等号:
SELECT * FROM order WHERE order_no = 'SO20240601001'; SELECT * FROM user WHERE status != 0;模糊匹配用LIKE,这个在测试里出现频率很高。你只知道订单号前缀是“SO2024”,想把这批单子都拉出来,就可以写:
SELECT * FROM order WHERE order_no LIKE 'SO2024%';%表示任意字符,放在前面就是“以什么结尾”,放在两边是“包含什么”。在集合里匹配用IN:
SELECT * FROM user WHERE status IN (0, 1, 3);范围查询用BETWEEN,检查金额、时间区间的时候最顺手:
SELECT * FROM payment WHERE amount BETWEEN 100 AND 500;组合条件就是AND和OR连用,但这里有一个几乎所有人初学都会踩的坑:AND优先级比OR高,不加括号的话查询条件会被拆得很奇怪。比如你想查“状态为1且金额大于100,或者状态为3”的记录,如果写成:
SELECT * FROM payment WHERE status = 1 OR status = 3 AND amount > 100;实际执行的意思就变成了“status为1,或者(status为3且金额大于100)”,和你的预期完全不同。正确写法要给OR条件加括号:
SELECT * FROM payment WHERE (status = 1 OR status = 3) AND amount > 100;测试人员写条件查询,一定要自己在心里翻译一遍逻辑,别把条件堆上去就以为万事大吉。
再说一个NULL的判断,这个坑十个人里九个踩。查询某个字段为空的数据,写成WHERE status = NULL永远查不到结果,因为NULL不参与等值比较。正确写法是:
SELECT * FROM user WHERE status IS NULL; SELECT * FROM user WHERE status IS NOT NULL;你在测试环境明明看到某条数据该字段是空的,用等号一查却是空白,多半就是写错了这个地方。
2.3 排序、去重、限量:让查询结果更像一份测试报告
拿到查询结果之后,你经常还要做二次处理。比如看最新的一条记录、看金额最大的记录、看有没有重复数据,这时候就要用到排序、去重和限量。
排序用ORDER BY,配合ASC(升序)和DESC(降序)。测试中检查最大最小、最新最旧时很实用:
SELECT order_no, amount FROM payment ORDER BY amount DESC; SELECT * FROM user ORDER BY create_time DESC;去重用DISTINCT。比如你要确认商品表里SKU是否出现了重复记录:
SELECT DISTINCT sku FROM goods;限量在MySQL里用LIMIT,在SQL Server里用TOP。这一点很多跨数据库学习的测试人员会记混。MySQL写法:
SELECT * FROM user LIMIT 5;SQL Server写法:
SELECT TOP 5 * FROM user;在功能测试中,LIMIT最常用的场景就是分页验证。页面每次展示10条数据,你就用LIMIT 10看前10条,核对顺序和字段内容。
这三样技能组合起来,你就能写出像样的测试查询了。下面这个语句是功能测试中很典型的“组合拳”:先过滤掉无关数据,再按时间倒序,最后只看前10条。
SELECT order_no, amount, create_time FROM payment WHERE status = 1 ORDER BY create_time DESC LIMIT 10;执行顺序是:先定位到表,然后通过WHERE过滤行,再按指定字段排序,最后限制返回条数。SQL的书写顺序和执行顺序不一样,但测试阶段你只要记住“先过滤、再排序、后限量”这个口诀就够用了。
2.4 查询语句在测试用例里的典型用法
光会写语法还不行,得知道什么时候用。我挑几个测试用例里最常见的查询场景。
第一个是数据落库断言。注册一个新用户后去用户表验证,脚本页面上显示成功不重要,重要的是user表里真的多了一条记录,且字段内容正确。这种场景就是“INSERT完之后SELECT出来比对”,这也是为什么标题里把查询和增加配成一对的原因。
第二个是状态流转验证。订单支付后,订单表里的status字段应该从1变成2。执行:
SELECT status FROM order WHERE order_no = 'SO20240601001';返回结果是你期望的2,用例才算通过。页面上的“支付成功”可能写死了,只有数据库里的状态才是诚实的。
第三个是列表数量核对。页面显示“共有5条记录”,接口返回的total是5,数据库里实际有多少?用COUNT(*)最直接:
SELECT COUNT(*) FROM user WHERE status = 1;COUNT(*)返回的是行数,这是测试人员常用的聚合函数,面试也经常考。它不查具体内容,只回答“到底有几条”这个问题,做数量断言正好。
3. INSERT 增加命令:两种写法的差别与测试场景下的正确姿势
3.1 完整字段写法:最稳妥,任何时候都推荐
聊完查,接下来聊增。INSERT的作用就是往表里增加一行数据。最基础、也最推荐的写法,是把字段名列出来,再一一对应写值:
INSERT INTO user (username, phone, status) VALUES ('test_001', '13800138000', 1);这句SQL的意思很直白:在user表里新增一条记录,username字段填test_001,phone字段填13800138000,status字段填1。这个写法在测试工作中几乎可以覆盖所有造数需求。
为什么我一直推荐完整字段写法?因为它明确表达了“我要给哪些列赋值”,即使表结构有几十个字段,你只写了三个,其他没写的字段就会用默认值或NULL。这个行为是可预期的、可控的。
这里有几个细节必须注意。字符串和日期类型的值一定要加单引号,数字类型可以不加,这是新手最容易翻车的地方。自增主键不要出现在字段列表里,一般让数据库自己生成,你写进去反而容易报错。字段顺序和VALUES里的顺序必须一一对应,这个顺序是可以打乱的,只要你前后对应就行。
比较隐蔽的一个坑是:有些测试同学从开发同学那里抄了一段INSERT,没看字段就直接用,结果写出来的数据其实被放到了错误的列里。因为数据库在字段类型匹配时比较宽容,比如两个字符串字段,你把username和phone的值互换了位置,它根本不报错。所以每写完一句INSERT,我都建议你紧跟着回头看一遍字段对应关系。
3.2 省略字段写法:省事但暗藏隐患
第二种写法是省略字段列表,直接把所有值按表结构顺序列出:
INSERT INTO user VALUES ('test_002', '13800138002', 1);这种写法在表结构简单、字段顺序明确的时候确实省事,但它有三个隐患,测试环境里我一般不建议用。
第一个隐患是,它要求你把表的所有列都写出来,包括自增主键。如果表有8个字段,你VALUES里也必须写8个值,少一个都会报错:column count doesn't match。表结构越复杂,这种写法越容易出错。
第二个隐患,也是更严重的,是它不是“列名对应”,而是“位置对应”。表结构一旦调整了字段顺序,你的老INSERT语句不会报错,但值会全部插到错位的地方。这种错误特别阴险,因为数据表面看是“进去了”,实际上全是脏数据,后面用SELECT查的时候会被带偏。
第三个隐患是,如果你不清楚表里还有哪些列,省略字段写法很容易漏掉那些有默认值或非空限制的字段,执行时直接报非空约束错误或默认值不符合预期。
所以我的建议很简单:测试环境自己做数据,坚持用完整字段写法。别图省事,省下的几秒钟可能换来半小时排错。
3.3 一次插入多条数据:测试造数的高效手段
功能测试里经常要造批量数据,比如测分页需要三五十条记录,一条一条INSERT能点到手软。这时候可以一次性插入多条:
INSERT INTO user (username, phone, status) VALUES ('test_001', '13800000001', 1), ('test_002', '13800000002', 1), ('test_003', '13800000003', 1);这个写法在MySQL、PostgreSQL、SQL Server里基本通用,只是个别老版本SQL Server可能不支持,具体以你们项目实际库为准。实测下来,一次插几十条完全没问题,效率比逐条执行高很多。
如果碰到要造几百条的极端场景,我会在测试工具里写个循环脚本,不断拼这种多行INSERT,或者用存储过程生成。不过这是后话了,初期你先把三五行的手工多行插入用熟,已经能解决80%的分页测试需求。
有一件事必须放在心里:在你执行INSERT批量造数之前,先想清楚这些数据后面用不用得到。测完之后最好自己清理掉,别在你的测试库留下大量垃圾数据。这也是为什么我说测试人员对数据库的操作边界感要强——不是所有数据都能随手乱加的。
3.4 数据插入后如何确认没插错:结合SELECT验证
INSERT和SELECT从来不是孤立使用的。我自己的标准工作流是三步走。
第一步,插入前先查询同条件的数据是否存在。万一测试环境里已经有一条相同username的数据,你再插一条,主键冲突直接报错,或者因为唯一索引原因插不进去。就算不是唯一索引,造重复数据也会污染后续查询结果。所以先跑一句:
SELECT * FROM user WHERE username = 'test_001';确认结果为空,再执行INSERT。
第二步,插入后立刻回查:
SELECT * FROM user WHERE username = 'test_001';看到这条数据实实在在出现在表里,字段值也正确,这次INSERT才算真正成功。
第三步,如果回查查不到,不要慌,按顺序排查几个常见原因:是不是插入语句执行报错了?是不是当前事务没提交?是不是连错库了?有些测试环境配置了触发器或默认值,插入后数据被自动修改了也有可能。这时候把SELECT的条件放宽,先按字段值模糊查,再看是不是被触发器改了状态。
“插入后回查”这个习惯,是我带新人时反复强调的。很多测试同学执行完INSERT,看到“Query OK”就以为完事了,等用例跑挂了才发现数据根本没进去或者内容不对。多花三秒钟回查一下,能省掉后面一长串排查时间。
4. 每天5分钟的训练安排:从只会看数据到独立完成验证
4.1 先准备一张练习表和十几条数据
空谈语法不如动手练。学习SQL,最好有一个能随便折腾的练习环境。我建议你三条路选一条:本地装一个MySQL或SQL Server都行,或者用测试环境里一张你熟悉的表,再不然用SQLite这种免安装的轻量库起步。关键是有一张表、有数据、有执行窗口,练错了也不心疼。
为了练习方便,可以建一张简单的用户表,包含这几个字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | 自增整数 | 主键 |
| username | 字符串 | 用户名 |
| phone | 字符串 | 手机号 |
| status | 整数 | 状态,1正常 0禁用 |
| amount | 小数 | 账户余额 |
| create_time | 时间 | 创建时间 |
往里插入十几条不同状态的数据,有正常用户、有被禁用的、有余额为0的、有余额上千的,数据越杂,练习越有效果。之后每天的5分钟训练,都在这张表上进行。
这里多说一句:如果公司测试环境没有自己的练习表,你就找实际业务表练,但只做SELECT,别随便INSERT。做增操作之前,先找leader确权,拿到可写权限再用。
4.2 第一周:只做SELECT,把基础打牢
第一周的目标是,不看任何参考,能独立写出基础SELECT。
- 第一天:
SELECT * FROM user;看全部数据,熟悉表结构。 - 第二天:
SELECT username, phone FROM user;学会只取需要的列。 - 第三天:
SELECT * FROM user WHERE username = 'test_001';精确查找一条。 - 第四天:
SELECT * FROM user WHERE status = 1 AND amount > 50;组合条件过滤。 - 第五天:
SELECT * FROM user ORDER BY create_time DESC LIMIT 5;按时间取最新几条。
五天下来,你已经能应对“查数据”的基本需求了。每天5分钟,其实只够写三条SQL、看一遍结果,但坚持一个星期,手感和肌肉记忆就建立了。这比周末花三小时死记硬背语法要有效得多。
4.3 第二周:加入去重、统计和模糊查找
第二周开始加入函数和运算符,目标是能回答“有多少条”“哪几个状态”“名字像什么”这类业务问题。
- 第一天:
SELECT COUNT(*) FROM user WHERE status = 1;统计正常用户数量。 - 第二天:
SELECT DISTINCT status FROM user;看看表里一共有哪几种状态。 - 第三天:
SELECT * FROM user WHERE username LIKE 'test%';模糊匹配用户名。 - 第四天:
SELECT * FROM user WHERE status IN (0, 1) AND amount BETWEEN 10 AND 100;组合IN和BETWEEN。 - 第五天:把你工作中真实遇到过的“想查但不会查”的需求列出来,逐个匹配语法。
第五天这个练习特别推荐。你在项目里一定有无数个瞬间想着“这个数据如果我能查一下就好了”,把它们记录下来,就是最贴合你工作场景的练习册。SQL不是你学会语法再去套业务,而是业务倒逼你把语法用熟。
4.4 第三周:上手INSERT,并养成回查习惯
第三周开始做增操作,目标是能独立造数,并且造完能自己确认。
- 第一天:用完整字段写法插入一条用户数据,然后用SELECT回查。
- 第二天:一次插入3条数据,回查确认3条都在。
- 第三天:故意把一个字符串的引号去掉,观察报错信息,理解引号的作用。
- 第四天:尝试插入一条status为空的数据,然后查一下NULL显示效果。
- 第五天:模拟一个场景——先造一个状态为1的已支付用户,再查询他的订单列表(前提是相关表有关联数据)。
我特别鼓励你第二天和第三天。很多人学INSERT只学“怎么写”,不学“写错了怎么办”。而实际工作中,你的INSERT一定会报错,报错信息才是你最好的老师。把常见报错都遇到过一遍,以后就不会怕了。
4.5 第四周:综合挑战和面试题自测
第四周开始做综合题,把前面积累的能力串起来:
- 按状态分组统计用户数量。
- 查出余额最高的用户。
- 查出用户名重复的用户记录。
- 先造10条测试订单数据,再按订单状态查询并排序。
- 自己给自己出一道“验证注册功能”的题:插入一个新用户,立刻回查,核对字段。
这五天做下来,你已经具备了独立完成“数据验证”和“测试造数”这两项核心能力。一个月后回头看,你会发现之前连数据库工具都不太敢打开的自己,已经能自信地在群里回复开发:“数据我查过了,确实有问题,这条记录在这里。”
5. 容易写错的SQL细节和测试面试常见考法
5.1 测试人员高频写错的三处语法
第一,字符串漏写引号。这是新手报错率最高的点。WHERE username = 张三会直接报列不存在,因为“张三”被当成了字段名。正确的做法是用单引号包裹字符串:WHERE username = '张三'。注意有些同学习惯用双引号,在MySQL严格模式下双引号也会被当成字符串,但为了兼容性,统一用单引号最稳。
第二,中英文标点混用。从Word、微信聊天记录里复制SQL时,最容易把括号、逗号、分号复制成全角符号,数据库直接报语法错误。你盯着看半天都看不出问题,因为肉眼分辨不出来。我的习惯是:SQL一律手敲,不复制;复制来的,先检查一遍符号再执行。
第三,表名或字段名和关键字冲突。user、order、status、desc这些词在有些数据库里是保留字,直接使用会报错。MySQL里用反引号括起来可以解决:
SELECT * FROM `order` WHERE `status` = 1;SQL Server则用方括号:
SELECT * FROM [order] WHERE [status] = 1;这个问题在老系统里尤其常见,所以看到order表别惊讶,加上反引号就好。
5.2 面试中考的SQL题,其实万变不离其宗
软件测试面试,特别是银行、金融、电商类项目,SQL几乎是必问项。我见过的面试题来来回回就是这几个套路:
- 统计数量:
SELECT COUNT(*) FROM user WHERE status = 1; - 去重统计:
SELECT COUNT(DISTINCT username) FROM user; - 按状态分组统计:
SELECT status, COUNT(*) FROM order GROUP BY status; - 取最大金额记录:
SELECT * FROM payment ORDER BY amount DESC LIMIT 1; - 查找重复数据:
SELECT username, COUNT(*) FROM user GROUP BY username HAVING COUNT(*) > 1;注意,上面的HAVING是用来过滤分组结果的,它和WHERE的区别是:WHERE过滤的是原始行,HAVING过滤的是分组后的结果。这个区别面试官很喜欢追问,你能一句话说清,分就拿到了。
面试官真正想看的,不是你会不会背语法,而是你能不能把一个业务问题翻译成SQL逻辑。比如他问“每个状态下的订单数量分别是多少”,你脑子里先想:按状态分组,然后数每一组有多少行,然后写GROUP BY + COUNT。这个翻译过程比默写重要得多。
至于SQL注入这类安全相关的问题,面试偶尔会问“什么是预编译”“为什么不能拼接SQL”,测试同学理解到“SQL拼接会导致安全风险,参数化查询可以避免”这个程度就够了,不用往深处钻。先把最基础的查询写对,安全话题属于加分项,不是必选项。
5.3 从“每天5分钟”到真正上手:下一步还能学什么
把查询和增加练熟之后,SQL的下半程通常是三件事:UPDATE改数据、DELETE删数据、JOIN多表关联。
UPDATE在测试中的典型用途是修改状态流转数据,比如把一个订单直接改成“已取消”,用来测试取消后的展示逻辑。DELETE主要用于清理自己造的测试数据,但执行前一定要确认WHERE条件足够精准,不然误删全表就是重大事故。我见过不止一次因为少写条件把整张表清空的案例,所以个人建议:测试环境也要养成先SELECT确认范围、再执行DELETE的习惯。
JOIN则是多表查询的入口。比如订单表里只有user_id,你要在结果里同时显示用户名,就得关联用户表:
SELECT o.order_no, u.username FROM order o JOIN user u ON o.user_id = u.id WHERE o.status = 1;掌握了JOIN,你排查数据问题的能力会再上一个台阶,很多“开发说数据没问题但页面就是不对”的疑难问题,最后都是靠JOIN拼出完整链路才定位到的。
但请记住,贪多嚼不烂。先把查询和增加这两类命令用成本能,再往外扩。试想一下,你连最基础的SELECT都没跑熟,就开始研究JOIN和存储过程,遇到报错根本不知道是基础语法问题还是关联逻辑问题,排查起来会非常痛苦。
我个人带新人的经验是:能把单表的增查用对、用稳,就已经超过相当一部分测试从业者了。很多人工作了三五年,遇到要查数据库还是只会截图给开发,甚至只会在界面上点点点。你每天花5分钟,一个月后就和他们拉开了差距。SQL没有想象中那么神秘,它就是你和数据之间的对话,而对话的第一步,就是敢开口问一句:“喂,表里到底有什么?”