SQL实战:测试必会的增删改查,从入门到熟练

本文是「软件测试零基础入门系列」的第二篇。上一篇我们认识了测试是什么,这一篇来搞定测试人的基本功——SQL。为什么测试必须会SQL?因为你要造测试数据、查数据库验证结果、定位Bug。不会SQL,你的测试能力是有短板的。

目录

  1. 一、为什么测试必须会SQL
  2. 二、数据库基础概念(3分钟速览)
  3. 三、环境准备:5分钟安装MySQL
  4. 四、查询数据:SELECT(最常用的语句)
  5. 五、条件查询:WHERE(筛选你想要的)
  6. 六、排序和分页:ORDER BY + LIMIT
  7. 七、聚合查询:COUNT/SUM/AVG/MAX/MIN + GROUP BY
  8. 八、多表查询:JOIN(测试必会难点)
  9. 九、子查询:查询里套查询
  10. 十、插入数据:INSERT(造测试数据)
  11. 十一、修改数据:UPDATE(改数据验证)
  12. 十二、删除数据:DELETE(清理测试数据)
  13. 十三、测试工作中SQL实战场景
  14. 十四、避坑指南
  15. 十五、总结与记忆口诀

一、为什么测试必须会SQL

1.1 测试工作中这些场景都需要SQL

场景1:前端显示订单金额100元,你怎么确认数据库里确实是100元? → 查数据库:SELECT amount FROM orders WHERE order_id = 'xxx'; 场景2:测试注册功能,怎么造100个测试账号? → 写SQL批量插入:INSERT INTO users ... 循环100次 场景3:用户说他的积分变少了,怎么核实? → 查积分变动日志:SELECT * FROM point_log WHERE user_id = 'xxx'; 场景4:开发改了订单表结构,你怎么确认数据迁移正确? → 对比新旧数据:SELECT COUNT(*), SUM(amount) ... 场景5:测试删除功能,删完后怎么确认真的删了? → 查数据库:SELECT * FROM table WHERE id = 'xxx'; -- 应该查不到

1.2 会SQL和不会SQL的区别

场景不会SQL的测试会SQL的测试
验证数据只能看前端,前端不显示就没办法直接查数据库,数据一目了然
造数据手动在页面一条条录入写SQL批量造,10秒搞定100条
定位Bug描述现象:"好像数据不对"精确描述:"order表status字段没更新"
造特殊数据页面限制造不出SQL直接插入任意数据
数据核对肉眼对比SQL对比,又快又准

一句话:不会SQL,你只能测前端显示的东西;会SQL,你能测数据本身。


二、数据库基础概念(3分钟速览)

2.1 什么是数据库

简单理解: 数据库 = 一个超级Excel文件 表(Table) = Excel里的一个Sheet页 行(Row) = Sheet里的一行数据 列(Column) = Sheet里的一列(字段) 比如一个用户表: ┌────┬──────────┬──────────┬─────────────┐ │ id │ username │ password │ email │ ← 列(字段) ├────┼──────────┼──────────┼─────────────┤ │ 1 │ admin │ 123456 │ a@test.com │ ← 行(记录) │ 2 │ test01 │ 111111 │ b@test.com │ │ 3 │ test02 │ 222222 │ c@test.com │ └────┴──────────┴──────────┴─────────────┘

2.2 关系型数据库

常见的数据库: ├── MySQL ← 最常用,开源免费(本篇主讲) ├── Oracle ← 大公司用,收费贵 ├── SQL Server ← 微软家的 ├── PostgreSQL ← 功能强大的开源数据库 └── SQLite ← 轻量级,移动端常用 好消息:它们90%的SQL语法是通用的,学会MySQL基本就都会了。

2.3 主键和外键

主键(Primary Key): └── 每行数据的唯一标识,不能重复,不能为空 └── 通常用id字段,自增数字 外键(Foreign Key): └── 关联另一个表的主键 └── 比如订单表里的user_id,关联用户表的id 示例: 用户表:id=1, username='张三' 订单表:order_id=1001, user_id=1 ← user_id就是外键,指向用户表的id

三、环境准备:5分钟安装MySQL

3.1 Windows安装

方案1:直接安装MySQL(推荐) 1. 去官网下载 MySQL Installer 2. 一路Next,设置root密码(记住!) 3. 安装MySQL Workbench(图形化工具) 方案2:使用集成环境(更简单) 1. 安装 phpStudy 或 Laragon 2. 一键启动MySQL 3. 自带phpMyAdmin管理界面

3.2 连接数据库

sql

复制

-- 方式1:命令行连接 mysql -u root -p -- 输入密码后进入 -- 方式2:用图形化工具 -- Navicat、DBeaver、MySQL Workbench 都可以 -- 填主机(127.0.0.1)、端口(3306)、用户名(root)、密码

3.3 创建练习用的数据库和表

sql

复制


四、查询数据:SELECT(最常用的语句)

4.1 基本查询

sql

复制

-- 查询所有列(* 表示全部列) SELECT * FROM users; -- 查询指定列 SELECT username, email FROM users; -- 查询结果: -- SELECT * FROM users; ┌────┬──────────┬──────────┬──────────────────┬─────┬──────┐ │ id │ username │ password │ email │ age │ city │ ├────┼──────────┼──────────┼──────────────────┼─────┼──────┤ │ 1 │ 张三 │ 123456 │ zhangsan@test.com│ 25 │ 北京 │ │ 2 │ 李四 │ 123456 │ lisi@test.com │ 30 │ 上海 │ │ 3 │ 王五 │ 123456 │ wangwu@test.com │ 28 │ 广州 │ │ 4 │ 赵六 │ 123456 │ zhaoliu@test.com │ 35 │ 深圳 │ │ 5 │ 孙七 │ 123456 │ sunqi@test.com │ 22 │ 北京 │ └────┴──────────┴──────────┴──────────────────┴─────┴──────┘

4.2 使用别名(AS)

sql

复制

-- 给列起别名(让查询结果更可读) SELECT username AS 用户名, email AS 邮箱 FROM users; -- 给表起别名(多表查询时很有用) SELECT u.username, u.city FROM users AS u;

4.3 去重(DISTINCT)

sql

复制

-- 查询所有城市(去重) SELECT DISTINCT city FROM users; -- 结果:北京、上海、广州、深圳 -- 查询有多少个不同的城市 SELECT COUNT(DISTINCT city) FROM users; -- 结果:4

4.4 测试常用查询速查

sql

复制

-- 1. 查某个用户是否存在 SELECT * FROM users WHERE username = '张三'; -- 2. 查今天注册了多少用户 SELECT COUNT(*) FROM users WHERE DATE(created_at) = CURDATE(); -- 3. 查订单总额 SELECT SUM(amount) FROM orders; -- 4. 查每个用户的订单数 SELECT user_id, COUNT(*) AS 订单数 FROM orders GROUP BY user_id;

五、条件查询:WHERE(筛选你想要的)

5.1 比较运算符

sql

复制

-- 等于 SELECT * FROM users WHERE city = '北京'; -- 不等于(两种写法) SELECT * FROM users WHERE city != '北京'; SELECT * FROM users WHERE city <> '北京'; -- 大于、小于、大于等于、小于等于 SELECT * FROM users WHERE age > 25; SELECT * FROM users WHERE age < 30; SELECT * FROM users WHERE age >= 25; SELECT * FROM users WHERE age <= 30;

5.2 逻辑运算符

sql

复制

-- AND(同时满足) SELECT * FROM users WHERE city = '北京' AND age > 20; -- OR(满足任一) SELECT * FROM users WHERE city = '北京' OR city = '上海'; -- NOT(取反) SELECT * FROM users WHERE NOT city = '北京'; -- 组合使用(用括号明确优先级) SELECT * FROM users WHERE (city = '北京' OR city = '上海') AND age > 25;

5.3 范围查询

sql

复制

-- BETWEEN(在...之间,包含边界) SELECT * FROM users WHERE age BETWEEN 25 AND 30; -- IN(在某个集合中) SELECT * FROM users WHERE city IN ('北京', '上海', '广州'); -- NOT IN(不在某个集合中) SELECT * FROM users WHERE city NOT IN ('北京', '上海');

5.4 模糊查询:LIKE

sql

复制

-- % 表示任意多个字符 -- _ 表示任意一个字符 -- 名字以"张"开头的 SELECT * FROM users WHERE username LIKE '张%'; -- 邮箱包含"test"的 SELECT * FROM users WHERE email LIKE '%test%'; -- 名字是两个字,且以"王"开头 SELECT * FROM users WHERE username LIKE '王_'; -- 常用场景:搜索功能测试 SELECT * FROM users WHERE username LIKE '%关键词%'; SELECT * FROM users WHERE email LIKE '%关键词%';

5.5 空值判断:IS NULL

sql

复制

-- 查询邮箱为空的用户 SELECT * FROM users WHERE email IS NULL; -- 查询邮箱不为空的用户 SELECT * FROM users WHERE email IS NOT NULL; -- 注意:不能用 = NULL,必须用 IS NULL

六、排序和分页:ORDER BY + LIMIT

6.1 排序

sql

复制

-- 按年龄升序(ASC,默认) SELECT * FROM users ORDER BY age ASC; -- 按年龄降序(DESC) SELECT * FROM users ORDER BY age DESC; -- 多字段排序:先按城市升序,同城市按年龄降序 SELECT * FROM users ORDER BY city ASC, age DESC; -- 测试场景:查最新订单 SELECT * FROM orders ORDER BY created_at DESC;

6.2 分页

sql

复制

-- LIMIT n:只返回前n条 SELECT * FROM users LIMIT 3; -- LIMIT offset, count:跳过offset条,取count条 SELECT * FROM users LIMIT 0, 2; -- 第1-2条(第1页) SELECT * FROM users LIMIT 2, 2; -- 第3-4条(第2页) SELECT * FROM users LIMIT 4, 2; -- 第5-6条(第3页) -- 分页公式:LIMIT (页码-1) × 每页条数, 每页条数 -- 测试场景:测试分页功能,直接查数据库确认总数 SELECT COUNT(*) FROM users; -- 总共5条,每页2条 = 3页

七、聚合查询:COUNT/SUM/AVG/MAX/MIN + GROUP BY

7.1 聚合函数

sql

复制

-- COUNT:计数 SELECT COUNT(*) FROM users; -- 总共多少用户 SELECT COUNT(email) FROM users; -- 有邮箱的用户数(不计NULL) -- SUM:求和 SELECT SUM(amount) FROM orders; -- 订单总金额 -- AVG:平均值 SELECT AVG(amount) FROM orders; -- 平均订单金额 SELECT AVG(age) FROM users; -- 平均年龄 -- MAX/MIN:最大/最小值 SELECT MAX(amount) FROM orders; -- 最贵订单 SELECT MIN(amount) FROM orders; -- 最便宜订单 SELECT MAX(age) FROM users; -- 最大年龄

7.2 分组统计:GROUP BY

sql

复制

7.3 分组后筛选:HAVING

sql

复制

-- WHERE 在分组前筛选,HAVING 在分组后筛选 -- 口诀:WHERE筛选行,HAVING筛选组 -- 查询订单总额超过2000的用户 SELECT user_id, SUM(amount) AS 消费总额 FROM orders GROUP BY user_id HAVING SUM(amount) > 2000; -- 查询有2个以上订单的用户 SELECT user_id, COUNT(*) AS 订单数 FROM orders GROUP BY user_id HAVING COUNT(*) >= 2;

7.4 SQL执行顺序(重要!)

写SQL的顺序: SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT 实际执行顺序: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT │ │ │ │ │ │ │ 找表 筛行 分组 筛组 选列 排序 分页 理解了执行顺序,就知道为什么WHERE不能用别名,ORDER BY可以。

八、多表查询:JOIN(测试必会难点)

8.1 为什么要多表查询

实际项目中,数据是分散在多个表里的: 用户表(users) 订单表(orders) ┌────┬──────┐ ┌────┬─────────┬──────────┬────────┐ │ id │ name │ │ id │ user_id │ product │ amount │ ├────┼──────┤ ├────┼─────────┼──────────┼────────┤ │ 1 │ 张三 │ ───┐ │ 1 │ 1 │ 手机 │ 2999 │ │ 2 │ 李四 │ ├───→│ 2 │ 1 │ 耳机 │ 299 │ │ 3 │ 王五 │ │ │ 3 │ 2 │ 电脑 │ 5999 │ └────┴──────┘ │ └────┴─────────┴──────────┴────────┘ 通过user_id关联 需求:"显示每个订单的用户名和产品名" → 需要把两个表连起来查

8.2 INNER JOIN(内连接)

sql

复制

8.3 LEFT JOIN(左连接)

sql

复制

-- 返回左表所有数据,右表匹配不上的填NULL -- 左表全量 SELECT u.username, o.product_name, o.amount FROM users AS u LEFT JOIN orders AS o ON u.id = o.user_id; -- 结果:所有用户都显示,没有订单的用户,订单字段为NULL -- 测试场景:查哪些用户没有订单 SELECT u.username FROM users AS u LEFT JOIN orders AS o ON u.id = o.user_id WHERE o.id IS NULL;

8.4 RIGHT JOIN(右连接)

sql

复制

-- 返回右表所有数据,左表匹配不上的填NULL -- 和LEFT JOIN相反,实际工作中LEFT JOIN更常用

8.5 JOIN对比总结

假设: users表有5条数据(用户1-5) orders表有8条数据(但只有用户1-4的订单,用户5有订单) INNER JOIN → 只返回有订单的用户(用户1-4的订单) LEFT JOIN → 返回所有用户,用户5的订单字段为NULL RIGHT JOIN → 返回所有订单,没有对应用户的订单(几乎不会发生)

8.6 多表JOIN实战

sql

复制

-- 三表联查:查每个订单的用户名和用户所在城市 SELECT u.username, u.city, o.product_name, o.amount FROM orders AS o INNER JOIN users AS u ON o.user_id = u.id ORDER BY o.created_at DESC; -- 测试场景:验证订单关联的用户信息是否正确 SELECT o.id AS 订单号, u.username AS 用户名, o.product_name AS 商品, o.amount AS 金额 FROM orders o INNER JOIN users u ON o.user_id = u.id WHERE o.id = 1;

九、子查询:查询里套查询

9.1 什么是子查询

sql

复制

9.2 子查询常见用法

sql

复制

9.3 子查询 vs JOIN

sql

复制

-- 同一个需求,两种写法 -- 需求:查下过单的用户信息 -- 子查询写法: SELECT * FROM users WHERE id IN (SELECT DISTINCT user_id FROM orders); -- JOIN写法: SELECT DISTINCT u.* FROM users u INNER JOIN orders o ON u.id = o.user_id; -- 选择建议:数据量大用JOIN(性能好),逻辑复杂用子查询(可读性好)

十、插入数据:INSERT(造测试数据)

10.1 基本插入

sql

复制

-- 插入单条 INSERT INTO users (username, password, email, age, city) VALUES ('测试用户1', 'test123', 'test1@test.com', 25, '杭州'); -- 插入多条 INSERT INTO users (username, password, email, age, city) VALUES ('测试用户2', 'test123', 'test2@test.com', 26, '成都'), ('测试用户3', 'test123', 'test3@test.com', 27, '武汉'), ('测试用户4', 'test123', 'test4@test.com', 28, '西安'); -- 查看插入结果 SELECT * FROM users ORDER BY id DESC;

10.2 插入查询结果

sql

复制

-- 把查询结果插入到另一个表 -- 场景:备份数据、复制数据 -- 假设有一个users_backup表 INSERT INTO users_backup SELECT * FROM users WHERE city = '北京';

10.3 测试造数据技巧

sql

复制

-- 技巧1:批量造测试账号(用存储过程或脚本循环) -- Python示例(后面会讲): -- for i in range(1, 101): -- sql = f"INSERT INTO users (username, email) VALUES ('test{i}', 'test{i}@test.com')" -- 技巧2:复制已有数据造新数据 INSERT INTO orders (user_id, product_name, amount, status) SELECT user_id, CONCAT(product_name, '(副本)'), amount, 'pending' FROM orders WHERE status = 'completed'; -- 技巧3:造边界值数据 INSERT INTO users (username, password, age) VALUES ('边界_最小年龄', 'test', 0), ('边界_最大年龄', 'test', 150), ('边界_超长名字', 'test', 30); -- username实际长度要测

十一、修改数据:UPDATE(改数据验证)

11.1 基本修改

sql

复制

-- ⚠️ 修改数据前一定先SELECT确认范围! -- 修改单条(必须带WHERE) UPDATE users SET email = 'newemail@test.com' WHERE id = 1; -- 修改多条 UPDATE users SET password = 'newpass' WHERE city = '北京'; -- 修改多个字段 UPDATE orders SET status = 'completed', amount = 2999.00 WHERE id = 1;

11.2 测试中的UPDATE场景

sql

复制

-- 场景1:模拟用户修改信息 UPDATE users SET email = 'updated@test.com' WHERE username = '测试用户1'; -- 场景2:批量修改订单状态(模拟发货) UPDATE orders SET status = 'shipped' WHERE status = 'pending'; -- 场景3:修改金额测试边界 UPDATE orders SET amount = 0.01 WHERE id = 1; -- 最小金额 UPDATE orders SET amount = 999999.99 WHERE id = 2; -- 最大金额 -- 场景4:测试完成后恢复数据 UPDATE orders SET status = 'pending' WHERE status = 'shipped';

11.3 ⚠️ 血的教训

sql

复制

-- 忘记加WHERE条件 → 全表更新!灾难! -- ❌ 危险操作: UPDATE users SET email = 'test@test.com'; -- 所有人邮箱都改了! -- ✅ 安全操作: -- 1. 先用SELECT确认范围 SELECT * FROM users WHERE city = '北京'; -- 确认是2条 -- 2. 再UPDATE UPDATE users SET email = 'test@test.com' WHERE city = '北京'; -- ✅ 开启事务(可以回滚) START TRANSACTION; UPDATE users SET email = 'test@test.com' WHERE city = '北京'; -- 确认没问题 COMMIT; -- 或者发现问题 ROLLBACK;

十二、删除数据:DELETE(清理测试数据)

12.1 基本删除

sql

复制

-- ⚠️ 删除比修改更危险!一定先SELECT确认! -- 删除单条 DELETE FROM users WHERE id = 10; -- 删除多条 DELETE FROM orders WHERE status = 'pending'; -- 删除所有数据(保留表结构) DELETE FROM test_table; -- 快速清空表(比DELETE快,重置自增ID) TRUNCATE TABLE test_table;

12.2 测试中的DELETE场景

sql

复制

-- 场景1:测试完清理数据 DELETE FROM users WHERE username LIKE '测试%'; -- 场景2:清理过期数据 DELETE FROM orders WHERE created_at < '2020-01-01'; -- 场景3:测试删除功能后验证 -- 先确认存在 SELECT * FROM users WHERE id = 5; -- 有一条 -- 执行删除(通过应用或SQL) DELETE FROM users WHERE id = 5; -- 确认已删除 SELECT * FROM users WHERE id = 5; -- 应该为空 -- 场景4:DELETE vs TRUNCATE DELETE FROM orders; -- 逐行删除,慢,可回滚,自增ID不重置 TRUNCATE TABLE orders; -- 直接清空,快,不可回滚,自增ID重置

十三、测试工作中SQL实战场景

13.1 场景1:验证注册功能

sql

复制

-- 1. 注册前确认用户不存在 SELECT * FROM users WHERE username = 'newuser'; -- 应该返回空 -- 2. 通过页面注册后,查数据库确认 SELECT * FROM users WHERE username = 'newuser'; -- 应该能查到一条记录,检查各字段是否正确 -- 3. 用相同信息再次注册,应该失败 -- 通过页面操作后,确认没有多一条数据 SELECT COUNT(*) FROM users WHERE username = 'newuser'; -- 应该还是1

13.2 场景2:验证订单金额计算

sql

复制

13.3 场景3:数据一致性核对

sql

复制

13.4 场景4:统计报表验证

sql

复制

13.5 场景5:接口测试数据验证

sql

复制

-- 接口测试中,调用接口后查数据库验证 -- 例:调用"创建订单"接口后 SELECT * FROM orders ORDER BY id DESC LIMIT 1; -- 检查返回的订单数据是否正确 -- 例:调用"删除用户"接口后 -- 确认用户已删除(物理删除) SELECT * FROM users WHERE id = 5; -- 应为空 -- 或确认用户已标记删除(逻辑删除) SELECT * FROM users WHERE id = 5; -- is_deleted应为1

十四、避坑指南

14.1 常见错误

表现正确做法
忘记WHEREUPDATE/DELETE全表先SELECT确认,再执行
用=比较NULLWHERE col = NULL查不出来IS NULL/IS NOT NULL
混用引号字符串没加引号字符串用单引号 '值'
JOIN条件写错笛卡尔积,数据量爆炸确认ON条件正确
GROUP BY漏字段SELECT的字段不在GROUP BY里非聚合字段都要放到GROUP BY
WHERE和HAVING混淆WHERE里用聚合函数聚合条件用HAVING
LIMIT不分页一次查全部数据大数据量必须分页
不备份就操作误删数据无法恢复重要操作前备份或开事务

14.2 测试环境操作铁律

1. 永远不要在线上数据库做写操作! 2. 修改/删除前,先SELECT确认影响范围 3. 重要操作使用事务(BEGIN → 操作 → 确认 → COMMIT / ROLLBACK) 4. 测试数据要有规律(如test_前缀),方便批量清理 5. 记录你改了哪些数据,测试完还原

十五、总结与记忆口诀

15.1 SQL语句速查表

查询:SELECT 列名 FROM 表名 WHERE 条件 ORDER BY 排序 LIMIT 分页 插入:INSERT INTO 表名 (列1, 列2) VALUES (值1, 值2) 修改:UPDATE 表名 SET 列1=值1 WHERE 条件 删除:DELETE FROM 表名 WHERE 条件 聚合:COUNT / SUM / AVG / MAX / MIN + GROUP BY + HAVING 连接:INNER JOIN / LEFT JOIN ... ON 条件 子查询:SELECT ... WHERE 列 IN (SELECT ...)

15.2 记忆口诀

增删改查四兄弟: SELECT查,INSERT增 UPDATE改,DELETE清 查前先想WHERE条件 改前SELECT看范围 删除之前备个份 安全第一记心间 GROUP BY跟着聚合走 HAVING管组不管行 JOIN连接两个表 ON条件不能忘

15.3 下一步学什么

掌握这些SQL,你已经能应对测试中90%的数据库操作了。 接下来建议: 1. 自己搭个MySQL,把本文的SQL全部手敲一遍 2. 找公司项目的测试库,练习查询和分析 3. 学习Python操作MySQL(pymysql),为自动化打基础

关于作者:专注软件测试领域,分享零基础入门到进阶的实战经验。欢迎在评论区交流讨论!

版权声明:本文为原创内容,欢迎转载,请注明出处。