
本文是「软件测试零基础入门系列」的第二篇。上一篇我们认识了测试是什么这一篇来搞定测试人的基本功——SQL。为什么测试必须会SQL因为你要造测试数据、查数据库验证结果、定位Bug。不会SQL你的测试能力是有短板的。目录一、为什么测试必须会SQL二、数据库基础概念3分钟速览三、环境准备5分钟安装MySQL四、查询数据SELECT最常用的语句五、条件查询WHERE筛选你想要的六、排序和分页ORDER BY LIMIT七、聚合查询COUNT/SUM/AVG/MAX/MIN GROUP BY八、多表查询JOIN测试必会难点九、子查询查询里套查询十、插入数据INSERT造测试数据十一、修改数据UPDATE改数据验证十二、删除数据DELETE清理测试数据十三、测试工作中SQL实战场景十四、避坑指南十五、总结与记忆口诀一、为什么测试必须会SQL1.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 │ atest.com │ ← 行记录 │ 2 │ test01 │ 111111 │ btest.com │ │ 3 │ test02 │ 222222 │ ctest.com │ └────┴──────────┴──────────┴─────────────┘2.2 关系型数据库常见的数据库 ├── MySQL ← 最常用开源免费本篇主讲 ├── Oracle ← 大公司用收费贵 ├── SQL Server ← 微软家的 ├── PostgreSQL ← 功能强大的开源数据库 └── SQLite ← 轻量级移动端常用 好消息它们90%的SQL语法是通用的学会MySQL基本就都会了。2.3 主键和外键主键Primary Key └── 每行数据的唯一标识不能重复不能为空 └── 通常用id字段自增数字 外键Foreign Key └── 关联另一个表的主键 └── 比如订单表里的user_id关联用户表的id 示例 用户表id1, username张三 订单表order_id1001, user_id1 ← user_id就是外键指向用户表的id三、环境准备5分钟安装MySQL3.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 │ zhangsantest.com│ 25 │ 北京 │ │ 2 │ 李四 │ 123456 │ lisitest.com │ 30 │ 上海 │ │ 3 │ 王五 │ 123456 │ wangwutest.com │ 28 │ 广州 │ │ 4 │ 赵六 │ 123456 │ zhaoliutest.com │ 35 │ 深圳 │ │ 5 │ 孙七 │ 123456 │ sunqitest.com │ 22 │ 北京 │ └────┴──────────┴──────────┴──────────────────┴─────┴──────┘4.2 使用别名ASsql复制-- 给列起别名让查询结果更可读 SELECT username AS 用户名, email AS 邮箱 FROM users; -- 给表起别名多表查询时很有用 SELECT u.username, u.city FROM users AS u;4.3 去重DISTINCTsql复制-- 查询所有城市去重 SELECT DISTINCT city FROM users; -- 结果北京、上海、广州、深圳 -- 查询有多少个不同的城市 SELECT COUNT(DISTINCT city) FROM users; -- 结果44.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 模糊查询LIKEsql复制-- % 表示任意多个字符 -- _ 表示任意一个字符 -- 名字以张开头的 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 NULLsql复制-- 查询邮箱为空的用户 SELECT * FROM users WHERE email IS NULL; -- 查询邮箱不为空的用户 SELECT * FROM users WHERE email IS NOT NULL; -- 注意不能用 NULL必须用 IS NULL六、排序和分页ORDER BY LIMIT6.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 BY7.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 BYsql复制7.3 分组后筛选HAVINGsql复制-- 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 JOINsql复制-- 同一个需求两种写法 -- 需求查下过单的用户信息 -- 子查询写法 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, test1test.com, 25, 杭州); -- 插入多条 INSERT INTO users (username, password, email, age, city) VALUES (测试用户2, test123, test2test.com, 26, 成都), (测试用户3, test123, test3test.com, 27, 武汉), (测试用户4, test123, test4test.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 fINSERT 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 newemailtest.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 updatedtest.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 testtest.com; -- 所有人邮箱都改了 -- ✅ 安全操作 -- 1. 先用SELECT确认范围 SELECT * FROM users WHERE city 北京; -- 确认是2条 -- 2. 再UPDATE UPDATE users SET email testtest.com WHERE city 北京; -- ✅ 开启事务可以回滚 START TRANSACTION; UPDATE users SET email testtest.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; -- 应该为空 -- 场景4DELETE 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; -- 应该还是113.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 BYWHERE和HAVING混淆WHERE里用聚合函数聚合条件用HAVINGLIMIT不分页一次查全部数据大数据量必须分页不备份就操作误删数据无法恢复重要操作前备份或开事务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操作MySQLpymysql为自动化打基础关于作者专注软件测试领域分享零基础入门到进阶的实战经验。欢迎在评论区交流讨论版权声明本文为原创内容欢迎转载请注明出处。