ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

软件测试工程师MySQL查询命令实战手册

软件测试工程师MySQL查询命令实战手册 软件测试工程师每天的工作除了写用例、提 Bug、跟开发对需求剩下很大一部分时间其实都在和数据打交道。不管是验证前端列表字段对不对、检查订单状态有没有更新还是定位线上数据异常都需要直接查数据库。MySQL 作为软件测试中最常遇到的数据库它的查询命令几乎是测试工程师的标配技能。这次这篇手册核心就是围绕软件测试实际工作场景把 MySQL 查询命令里最常用、最容易踩坑的部分梳理清楚。从连接数据库、基础 SELECT到 JOIN 多表关联、聚合分组、子查询、数据造数、性能排查都会覆盖到。文章里的示例尽量贴近测试日常比如查订单、查用户、统计接口返回数据量、验证一批测试数据是否入库方便你直接照着练习再用到工作中。如果你正在准备软件测试面试或者刚入行想系统补一下 MySQL 查询能力又或者做测试开发需要写数据校验脚本这篇文章都值得收藏。MySQL 查询不要求你会 DBA 级别的优化能力但要求你写得快、查得准、改得稳这也是本文想帮你达到的目标。1. 核心能力速览能力项说明适用人群软件测试工程师、测试开发、准备测试岗面试的候选人前置要求了解基本 SQL 语法熟悉测试流程即可主要功能数据查询、条件过滤、多表关联、聚合统计、数据造数、性能分析常用工具MySQL 命令行、Navicat、DBeaver、Workbench环境要求本地或测试环境 MySQL 5.7 / 8.0 均可启动方式命令行登录、GUI 工具连接批量能力支持批量导入测试数据、循环造数脚本适合场景接口测试数据校验、数据库断言、测试数据准备、线上问题定位学习成本从基础查询到常用实战场景一周内可以掌握下面所有示例均以一套简单的电商测试库表结构为基础表包含users用户表、orders订单表、products商品表方便统一演示。2. 适用场景与使用边界MySQL 查询命令在软件测试中的价值主要体现在四个场景。第一个场景是接口测试的数据校验。调用创建订单接口后需要确认订单记录真的写进orders表状态字段是否正确。这个时候直接在数据库里 SELECT 一条记录比看接口返回更可靠。第二个场景是测试数据准备与清理。执行自动化测试前要批量插入一批用户、商品、订单数据测试结束后要清理这些数据避免污染测试环境。这时 INSERT、DELETE、UPDATE 就非常常用。第三个场景是问题定位。开发说“这个 Bug 我本地复现不出来”测试手里往往有线上或者灰度环境的数据库查询权限可以直接查数据核对时间戳、状态流转、金额计算是否异常很多问题查一遍数据基本能判断是前端展示问题还是后端写入问题。第四个场景是造数统计与报表验证。测试报表功能时往往需要先算出数据库里的预期值再和页面展示做比对。聚合查询和分组查询就是这里的核心手段。注意使用边界。测试环境可以随便查、随便改但生产环境必须严格控制访问权限只读账号只做查询不执行 UPDATE 和 DELETE。涉及用户手机号、邮箱、身份证等敏感字段时日常查询也要脱敏处理不要导出到本地方便留存。所有数据操作都要在授权范围内进行这是测试工程师的基本数据安全底线。3. MySQL 测试环境准备不管是用命令行还是 GUI 工具测试环境准备的核心就三步装 MySQL、准备测试库、建好常用表。如果本地没有 MySQLWindows 可以下载 MySQL Installer安装时选择 Server only 即可。macOS 可以用 Homebrew 安装brew install mysql brew services start mysqlLinuxDebian/Ubuntu可以用 apt 安装sudo apt update sudo apt install mysql-server sudo systemctl start mysql安装完成后默认 root 用户需要设置密码。这里建议创建一个专用的测试账号避免所有操作都使用 rootCREATE USER testerlocalhost IDENTIFIED BY Test123; GRANT ALL PRIVILEGES ON test_db.* TO testerlocalhost; FLUSH PRIVILEGES;MySQL 8.0 默认使用caching_sha2_password认证插件如果使用 Navicat 等旧版本工具连接可能出现Authentication plugin caching_sha2_password cannot be loaded或2059错误。解决办法是修改账号认证方式ALTER USER testerlocalhost IDENTIFIED WITH mysql_native_password BY Test123; FLUSH PRIVILEGES;命令行连接数据库mysql -u tester -p -h 127.0.0.1 -P 3306 test_db参数解释-u指定用户-p提示输入密码-h指定主机-P指定端口最后的test_db是默认数据库名。端口默认 3306如果本机端口冲突可以改配置文件后用--port指定新端口。连接成功后先创建一个测试库再建两张最常用的表方便后续做联查实验CREATE DATABASE IF NOT EXISTS test_db DEFAULT CHARACTER SET utf8mb4; USE test_db; CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age INT, city VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, product_name VARCHAR(100), amount DECIMAL(10,2), status TINYINT COMMENT 1-待支付 2-已支付 3-已取消, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );插入一批基础测试数据INSERT INTO users (name, age, city) VALUES (张三, 25, 北京), (李四, 30, 上海), (王五, 28, 广州), (赵六, 35, 深圳), (孙七, 22, 北京); INSERT INTO orders (user_id, product_name, amount, status) VALUES (1, 手机, 1999.00, 2), (2, 耳机, 399.00, 1), (1, 充电器, 99.00, 3), (3, 手机, 2599.00, 2), (4, 电脑, 5999.00, 2), (5, 耳机, 299.00, 1), (3, 数据线, 39.00, 3), (4, 显示器, 1299.00, 2);建好表、插好数据之后剩下的查询练习就都能在这套表上操作了。4. 基础查询命令SELECT、WHERE、ORDER BY、LIMIT测试工程师用得最多的查询永远是基础三件套查哪些字段、过滤什么条件、排序和分页。4.1 查全部字段和指定字段SELECT * FROM users; SELECT id, name, city FROM users;SELECT *适合快速看表结构和数据但实际脚本里建议写明字段。一是性能更好二是当表结构变化时脚本结果更可控。4.2 WHERE 条件过滤最常见的测试校验场景是“查某一条数据是否按预期写入”SELECT * FROM orders WHERE order_id 1024;多条件组合时注意 AND 和 OR 的优先级。AND 的优先级高于 OR所以多个条件混用建议加括号SELECT * FROM orders WHERE status 2 AND (amount 100 OR product_name 耳机);在测试中“某个字段是否为空”也是一个高频判断条件。SQL 里判断空值不能用 NULL要用IS NULL或IS NOT NULLSELECT * FROM users WHERE age IS NULL; SELECT * FROM orders WHERE remark IS NOT NULL;这里要特别提醒一个新手容易踩的坑NULL和空字符串是两回事。 能查到空字符串但查不到NULL值反过来也一样。4.3 模糊查询 LIKE测试搜索功能时LIKE 几乎必用。比如验证前端搜索框能否按关键字匹配商品名SELECT * FROM orders WHERE product_name LIKE 手机%;%表示任意长度字符_表示单个字符。手机%匹配以“手机”开头的值%手机%匹配包含“手机”的值。如果搜索关键字本身包含%或_需要用 ESCAPE 转义SELECT * FROM products WHERE product_name LIKE %100\%% ESCAPE \\;4.4 排序 ORDER BY排序在测试中常用于“查最新一条记录”或“按金额从高到低核对页面展示顺序”SELECT * FROM orders ORDER BY created_at DESC; SELECT * FROM orders ORDER BY amount DESC, id ASC;多个排序字段时前面的字段优先。注意ORDER BY要放在WHERE之后否则语法报错。4.5 分页 LIMIT接口测试里经常要验证分页逻辑比如每页 10 条、取第 2 页SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 10;这里LIMIT 10表示返回 10 条OFFSET 10表示跳过前 10 条也就是第二页的数据。还有一种等价写法SELECT * FROM users ORDER BY id LIMIT 10, 10;第一个数字是偏移量第二个数字是返回条数。两种写法都常见建议熟记一种看懂另一种。另外需要注意MySQL 8.0.31 之前LIMIT后面不能直接跟表达式比如LIMIT 10 * 1会报错。如果脚本里需要动态计算分页可以在应用层先算好数字再拼接 SQL。5. 聚合查询与分组统计测试报表、统计类功能时聚合函数是核心工具。拿订单表举例5.1 COUNT 统计行数接口测试断言“创建订单后订单表多了一条记录”最常用SELECT COUNT(*) FROM orders WHERE user_id 1; SELECT COUNT(1) FROM orders WHERE status 2;COUNT(*)和COUNT(1)在 MySQL 中差别极小都可以用来统计行数。COUNT(字段名)则只统计该字段非 NULL 的行数比如统计有备注的订单数SELECT COUNT(remark) FROM orders;如果表里remark字段有 3 条 NULLCOUNT(remark)的结果会比COUNT(*)少 3。5.2 SUM、AVG、MAX、MIN测试金额汇总类功能时SUM 是标准手段SELECT SUM(amount) FROM orders WHERE status 2; SELECT AVG(amount) FROM orders WHERE product_name 手机; SELECT MAX(amount), MIN(amount) FROM orders;如果SUM的结果用于和页面对比注意金额字段建议使用DECIMAL类型直接用 FLOAT 做累加会产生精度差导致测试断言不稳定。5.3 GROUP BY 分组统计统计每个用户的订单数和总金额SELECT user_id, COUNT(*) AS order_count, SUM(amount) AS total_amount FROM orders GROUP BY user_id;这里AS是给结果列起别名方便脚本里按别名取值。测试中我们可以把自己算出来的结果和接口返回的统计数据一一比对发现数据对不上就说明逻辑层出了问题。5.4 HAVING 分组后过滤WHERE 是在分组前过滤HAVING 是在分组后过滤。例如只查订单数大于 1 的用户SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id HAVING COUNT(*) 1;注意HAVING可以使用聚合函数WHERE不能。如果想“筛选字段值为空”或“筛选非空”的分组数据同样可以用IS NULL判断。5.5 去重 DISTINCT测试中验证“某个字段是否有重复值”可以直接用 DISTINCTSELECT DISTINCT city FROM users; SELECT COUNT(DISTINCT user_id) FROM orders;这里顺带回答一个高频面试疑问OR不会去重去重需要靠DISTINCT或GROUP BY。WHERE city 北京 OR city 上海的结果可能包含重复行并不会自动合并。6. 多表关联查询 JOIN测试过程中最常遇到的不是单表查询而是“用户表、订单表、商品表”之间的关联。比如验证订单列表页显示的购买人昵称是否正确就要把orders和users联起来查。6.1 INNER JOIN 内连接只返回两张表中匹配上的数据SELECT o.id, u.name, o.product_name, o.amount FROM orders o INNER JOIN users u ON o.user_id u.id;测试中常见问题页面显示订单总数为 8 条直接查orders表是 8 条加了 JOIN 之后变成 10 条多出两条重复数据。这就是典型的user_id关联字段在users表中对应多条记录导致笛卡尔积放大。定位时可以先分别查单表确认数据量再逐步加关联条件对比。6.2 LEFT JOIN 左连接返回左表全部数据右表无匹配时字段为 NULL。比如查所有用户及其订单数没有下单的用户也要显示SELECT u.id, u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON u.id o.user_id GROUP BY u.id, u.name;没有订单的用户COUNT(o.id)结果为 0。这里要注意如果COUNT(u.id)就会统计出 1因为左表本身有一行这也是测试脚本里容易写错的地方。6.3 三表关联实际业务表往往不止两张。假设再加一张products表要查“每个用户买了哪些商品以及商品分类”可以用多个 JOIN 串联SELECT u.name, o.product_name, p.category FROM users u INNER JOIN orders o ON u.id o.user_id INNER JOIN products p ON o.product_id p.id WHERE o.status 2;执行顺序上MySQL 会先对表做笛卡尔积再逐步过滤。表数据量大时多表 JOIN 很容易拖慢查询这个场景放到后面性能排查部分讲。6.4 自连接有时候一张表内部就能完成关联。比如员工表里有manager_id指向同一张表的id查员工和上级姓名SELECT e.name AS employee_name, m.name AS manager_name FROM employees e LEFT JOIN employees m ON e.manager_id m.id;测试“上下级树形展示”之类的功能时会用到这种写法。7. 子查询与常用函数7.1 WHERE 子查询查“下单金额超过 1000 的用户姓名”SELECT name FROM users WHERE id IN ( SELECT user_id FROM orders WHERE amount 1000 );子查询的结果集不要太大否则性能很差。如果子查询可以改写成 JOIN测试脚本优先用 JOIN更容易通过EXPLAIN分析执行计划。7.2 标量子查询在 SELECT 后面直接查一个值比如查每个用户最近一笔订单金额SELECT u.name, (SELECT MAX(amount) FROM orders o WHERE o.user_id u.id) AS max_amount FROM users u;这种写法适合“一对一补充字段”的场景但是子查询会对每一行执行一次数据量大时明显变慢。7.3 常用字符串函数测试姓名、手机号、地址等字段时很常用SELECT CONCAT(name, -, city) FROM users; SELECT UPPER(name), LOWER(city) FROM users; SELECT LENGTH(name) FROM users; SELECT SUBSTRING(name, 1, 1) FROM users; SELECT TRIM(name) FROM users;7.4 时间函数验证订单是否在预期时间范围内生成SELECT * FROM orders WHERE created_at 2025-01-01 00:00:00 AND created_at 2025-02-01 00:00:00; SELECT DATE(created_at), COUNT(*) FROM orders GROUP BY DATE(created_at);需要注意created_at 2025-01-01和created_at 2025-01-01的边界差异。前者包含当天 0 点后者不包含。测试时间范围场景最容易出 Bug 的地方就是边界值。7.5 CASE WHEN 逻辑判断CASE WHEN 可以帮我们把数据库里的状态数字映射成可读文本比如订单表status字段 1、2、3 分别表示待支付、已支付、已取消SELECT id, product_name, amount, CASE status WHEN 1 THEN 待支付 WHEN 2 THEN 已支付 WHEN 3 THEN 已取消 ELSE 未知 END AS status_text FROM orders;测试中这个结果可以直接和页面展示的状态文案做比对。如果数据库映射和页面文案不一致那就是前后端枚举值不对齐的问题。8. 测试数据准备INSERT、UPDATE、DELETE测试执行之前要造数据测试结束之后要清理数据这部分的 SQL 使用同样重要。8.1 批量插入造数直接用多条 INSERT 可以一次插入多行INSERT INTO users (name, age, city) VALUES (测试用户A, 20, 北京), (测试用户B, 21, 上海);如果需要插入几千条数据手写不现实可以用存储过程循环造数。下面是一个例子批量插入 100 个测试用户DELIMITER // CREATE PROCEDURE insert_test_users(IN count INT) BEGIN DECLARE i INT DEFAULT 1; WHILE i count DO INSERT INTO users (name, age, city) VALUES (CONCAT(auto_user_, i), 20 i, 测试城市); SET i i 1; END WHILE; END // DELIMITER ; CALL insert_test_users(100);使用完可以删除存储过程DROP PROCEDURE IF EXISTS insert_test_users;存储过程的优势是重复执行方便适合自动化测试套件里的数据准备阶段。缺点是调试麻烦建议只在测试环境使用。8.2 UPDATE 更新数据把某个订单状态从待支付改成已支付模拟用户完成支付动作UPDATE orders SET status 2, pay_time NOW() WHERE id 1024;测试中执行 UPDATE 前一定要先写 WHERE 条件再执行。可以先 SELECT 一遍确认影响行数再执行 UPDATE。禁止不带 WHERE 全表更新这是测试环境搞崩溃最常见的操作。8.3 DELETE 清理数据清理测试产生的数据DELETE FROM orders WHERE user_id IN (SELECT id FROM users WHERE name LIKE auto_user_%); DELETE FROM users WHERE name LIKE auto_user_%;注意如果表之间有外键约束DELETE 可能会因为子表引用而失败。此时先删子表数据再删主表数据。8.4 事务与回滚测试数据修改时建议包在事务里验证完再决定提交还是回滚START TRANSACTION; UPDATE orders SET amount amount 100 WHERE id 1024; SELECT * FROM orders WHERE id 1024; ROLLBACK;ROLLBACK之后UPDATE不会真正生效。这个特性很适合做“数据变更验证”不会污染测试数据。9. 性能排查与索引验证测试工程师不一定需要做数据库调优但遇到接口超时、查询卡住时至少要能判断是 SQL 写法问题还是索引缺了。9.1 EXPLAIN 查看执行计划在任意 SELECT 前面加EXPLAIN可以看到这条 SQL 的执行方式EXPLAIN SELECT * FROM orders WHERE user_id 1;重点看type和rows两列。type从好到差大致是const、eq_ref、ref、range、index、ALL。ALL表示全表扫描表数据量大时基本都会慢。rows是预估扫描行数行数越少越好。如果发现user_id查询走全表扫描可以建索引优化CREATE INDEX idx_user_id ON orders(user_id);测试中验证联表查询慢的问题第一步就查两张表的关联字段有没有索引这个习惯能省很多排查时间。9.2 慢查询日志MySQL 可以开启慢查询日志记录执行时间超过阈值的 SQL。对于测试环境复现接口慢问题很有帮助SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 2;执行完后通过日志文件定位慢 SQL再看它的执行计划。注意这些设置是全局动态配置MySQL 重启后会失效生产环境改配置需要走变更流程。9.3 锁表问题测试过程中偶尔会遇到“查询卡死”的情况多半是某个事务持有锁没释放。可以查当前有哪些事务在跑SELECT * FROM information_schema.INNODB_TRX;如果有长时间运行的事务可以拿到trx_mysql_thread_id后确认不是正式任务再终止KILL 12345;这里提示一个测试常见的“坑”在 Navicat 或命令行里手动执行了UPDATE但没有提交事务窗口没有关闭锁就会一直持有其他查询全部阻塞。遇到“SQL 执行一直转圈”先检查有没有未提交事务。10. 测试工作流中的 MySQL 实战场景把上面的命令组合起来对应到软件测试不同环节里更直观。10.1 接口测试数据断言接口调用后从数据库验证写入结果。这是最基本的数据库断言写法-- 期望创建订单接口成功后orders 表新增一条 status1 的记录 SELECT COUNT(*) FROM orders WHERE user_id 1001 AND status 1 AND created_at NOW() - INTERVAL 5 MINUTE;如果断言结果为 1说明写入成功如果为 0需要排查是接口没调用成功还是 SQL 判断条件有误。在自动化测试框架中可以在断言里拼这样的 SQL 语句也可以在测试后置脚本里执行清理 SQL。10.2 造数后页面列表校验批量插入测试用户后验证用户管理列表的分页展示是否正确SELECT COUNT(*) FROM users WHERE name LIKE auto_user_%;前端每页显示 10 条翻到第 3 页数据库里应该展示第 21 到第 30 条。通过ORDER BY id LIMIT 10 OFFSET 20能查出这一页的预期数据直接和页面比对。10.3 报表数据核对测试订单统计报表时用聚合查询算出预期值SELECT DATE(created_at) AS day, SUM(amount) AS total FROM orders WHERE status 2 GROUP BY DATE(created_at) ORDER BY day;拿这个结果和报表页面展示的每日销售额做比对如果对不上再进一步核对是不是有退款订单没排除、时区换算问题、状态枚举不同等。10.4 线上问题定位线上反馈“订单金额不对”拿到授权后先用只读账号查询SELECT id, user_id, product_name, amount, status, created_at FROM orders WHERE id 888888;再查订单日志表、支付流水表判断是金额计算逻辑问题还是状态流转异常。这里要强调只读账号不要执行任何写操作即使看到数据错误也要通过工单反馈给开发而不是自己直接修数据。11. 常见问题与排查方法问题现象可能原因排查方式解决方案命令行连接 MySQL 报 Access denied用户名或密码错误或用户没有远程访问权限检查账号密码确认授权使用GRANT重新授权或连接正确的库Navicat 连接报 2059 错误MySQL 8.0 默认认证插件不兼容旧工具查看 MySQL 用户认证插件修改mysql_native_password或升级客户端查询结果中文乱码连接字符集和服务端字符集不一致检查连接编码连接时加--default-character-setutf8mb4SELECT 加了 WHERE 却返回空字段值包含隐藏空格或大小写差异用TRIM()、LOWER()处理字段后查询调整查询条件或先确认原始数据UPDATE 执行后数据没变化条件不匹配或事务未提交先 SELECT 验证条件再检查事务状态先查询影响行数再执行更新DELETE 报外键约束失败子表仍引用要删除的记录查询子表关联数据先删子表再删主表查询很慢全表扫描、缺索引或锁等待执行 EXPLAIN 查看执行计划加索引、优化 SQL、检查锁LIMIT后面写表达式报错旧版本 MySQL 不支持 LIMIT 后接表达式检查 MySQL 版本在应用层先计算数字再拼接 SQLOR 查询结果出现重复行多条件 OR 可能导致行重复使用SELECT DISTINCT改用DISTINCT或GROUP BY字段名是关键字导致报错表字段命名使用了order、group等关键字查看错误日志用反引号包裹字段名12. 最佳实践与总结这里再补充几条软件测试工程师使用 MySQL 的实操建议。第一所有数据库操作尽量使用测试环境专用账号权限最小化。开发和 DBA 创建账号时只申请自己业务需要的库和表权限不要默认拿 root。第二脚本中执行的 SQL 习惯性带条件。哪怕是测试环境也不建议直接用DELETE FROM orders这种清表操作尽量明确 WHERE 条件再删。写完后先 SELECT 一下确保影响范围和预期一致。第三涉及金额、数量、时间等字段时注意数据类型的精度和边界。FLOAT比较在数据库里不可靠时间范围要用左闭右开[start, end)的形式去查避免边界值漏数据。第四遇到多表 JOIN 查询结果异常时先分别单表确认数据量再逐步缩小 JOIN 条件和 WHERE 条件定位是数据重复问题、字段关联错误还是过滤条件缺失。第五批量造数和数据清理建议封装成脚本或存储过程记录到测试资产库中。这样不同测试人员执行相同的造数任务时结果保持一致避免每个人手写 SQL 出现偏差。第六所有 SQL 脚本、造数文件、导出数据都建议纳入版本管理和测试用例一样走评审流程。涉及敏感数据导出时必须脱敏处理遵守公司和行业的隐私合规要求。MySQL 查询命令对于软件测试工程师来说不是“会不会写”的选择题而是“用得好不好”的加分项。建议先从最常用的 SELECT、WHERE、ORDER BY、LIMIT 开始练接着把 JOIN 和聚合查询用熟再针对接口测试、报表测试、性能排查这些具体场景把命令组合成自己的模板库。遇到任何一条 SQL 执行结果不符合预期就多问一句是查询条件错了还是数据本身有问题还是业务逻辑本来就是这样设计的带着这个思路去查库进步会非常快。
返回列表