news 2026/9/10 15:17:46

MySQL查询命令在软件测试中的四类场景实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL查询命令在软件测试中的四类场景实战指南

MySQL查询命令对软件测试工程师来说,真正重要的不是记住一堆语法,而是知道什么场景下用哪条命令去解决测试问题。这些年我在测试环境里做得最多的事情,无非四类:测试前造数、测试中校验、bug 定位时查数、测试后清数。如果你正在学 MySQL,或者简历上写了“熟练使用 SQL”但面试时被问住,这篇可以按实际工作顺序来读。下面按测试工作中的真实场景拆一遍,不背命令,只讲怎么用。

1. 测试工程师查 MySQL,先想清楚这四类需求

1.1 造数:测试前把数据准备到位

功能测试和接口测试经常需要特定状态的数据。比如你要测“已支付订单取消”的流程,界面上手动走到已支付状态可能要好几步,但如果测试库里已经有一批历史订单,直接查一条合适的订单就能继续测。

这种需求靠的就是查询命令先定位,再配合 INSERT 或 UPDATE 去造数据。注意一个原则:造数前先确认业务规则,不能直接把状态改成一个业务上不可能出现的值。我一般会先查同类型数据的字段长什么样,再照着改,这样至少不会把表结构搞错。

1.2 校验、定位、清理:测试后离不开这三件事

测试过程中最常做的事情就是拿界面显示的数字跟数据库里的实际值对比。

  • 界面显示订单总数 100 条,库里COUNT(*)是多少。
  • 界面显示总金额 5000 元,库里SUM(amount)是多少。
  • 某个用户看不到自己的订单,去订单表查这个人的 user_id 到底有没有记录,status 是什么,有没有被逻辑删除。

这些都靠查询命令完成。另外,测试会产生大量脏数据,比如批量注册了几百个用户,测试结束后要清掉,否则下一次回归数据对不上。清理不是随便 DELETE,要先查到这批数据的共同特征,确认影响范围后再处理。

1.3 先连接,再查表:环境、工具和表结构

先确保本地或测试机能连上 MySQL。命令行是最通用的方式:

mysql -h127.0.0.1 -P3306 -uroot -p

-h 是主机地址,-P 是端口,-u 是用户名,-p 表示回车后输入密码。如果你用的是 Navicat、DBeaver 或者 MySQL Workbench,填好连接信息就行。MySQL Workbench 自带 SQL 编辑器,要新建数据表可以直接在编辑器里写CREATE TABLE语句执行,不用非要切到图形界面。

连接之后,先确认自己到底在哪个库:

SHOW DATABASES; USE test_db; SHOW TABLES;

然后看表结构:

DESC orders; SHOW CREATE TABLE orders;

DESC能看到字段名、类型、是否允许 NULL 等信息。SHOW CREATE TABLE能看到建表语句,包含索引、字符集这类细节。测试环境连接不上时,优先检查网络、端口、密码和当前账号是否允许从当前 IP 访问。报Host '...' is not allowed to connect to this MySQL server就说明 IP 不在允许名单里,找库管理员加白名单,不要自己乱试。

2. 单表查询:筛选、排序、分页、去重

2.1 SELECT 基础结构:一次只查你需要的字段

单表查询是测试工程师使用频率最高的操作。基本结构是:

SELECT 字段1, 字段2 FROM 表名 WHERE 条件 ORDER BY 排序字段 LIMIT 行数;

先建议只查需要的字段,不要一上来就SELECT *。测试环境数据量可能不大,但生产环境查全表字段会有不必要的开销。更重要的是,只查目标字段,眼睛更容易看到关键信息。

WHERE 条件里最常用的是这些:

  • 等值判断:status = 1
  • 范围判断:amount > 100create_time >= '2025-01-01'
  • 集合判断:user_id IN (1001, 1002, 1003)
  • 模糊匹配:order_no LIKE 'TEST%'
  • 空值判断:remark IS NULL

这里注意LIKE里的%_不一样。%表示任意多个字符,_表示一个字符。比如order_no LIKE 'TEST_2025%'表示 TEST 后面必须有一个任意字符,再来 2025 开头才能匹配,写错会查不到结果。

2.2 ORDER BY 排序和 LIMIT 分页

排序语法本身很简单,但测试里有两个高频场景。

第一个是取最新一条记录。订单表通常有自增 id 或 create_time,可以直接:

SELECT id, order_no, user_id, amount, status, create_time FROM orders ORDER BY create_time DESC LIMIT 1;

DESC是倒序,ASC是升序。多个字段排序时,写在前面的是第一优先级。比如先按状态升序,再按时间倒序:

SELECT order_no, status, create_time FROM orders ORDER BY status ASC, create_time DESC;

第二个是分页。MySQL 的 LIMIT 语法有两种写法:

-- 从第 0 条开始取 10 条 SELECT * FROM orders ORDER BY id LIMIT 10 OFFSET 0; -- 等价写法:偏移量 0,取 10 条 SELECT * FROM orders ORDER BY id LIMIT 0, 10; -- 第二页 SELECT * FROM orders ORDER BY id LIMIT 10 OFFSET 10;

接口分页测试时,重点看第一页和第二页之间数据有没有重复或遗漏。导致重复的常见原因就是 ORDER BY 字段不唯一,比如只用 create_time 排序,同一秒有两条记录,分页后顺序就可能稳定。

2.3 DISTINCT 去重和“or 能去重吗”

很多面试题里会问:mysql 的 or 能去重吗。先给结论:不能。or是逻辑连接条件,不是去重逻辑。

举个例子:

SELECT name FROM user WHERE age < 30 OR age > 40;

如果两个不同 id 的用户恰好都叫“张三”,结果会返回两行“张三”,因为 MySQL 没有做任何去重动作。要去重只能主动写去重逻辑。常见方式有三种:

-- 方式一 SELECT DISTINCT name FROM user; -- 方式二 SELECT name FROM user GROUP BY name; -- 方式三 SELECT name FROM user UNION SELECT name FROM user;

UNION默认去重,UNION ALL不去重。DISTINCTGROUP BY都能去重,区别在于GROUP BY一般用于分组统计,DISTINCT更适合单纯去重。

测试里常用 DISTINCT 做数据检查。比如查某个状态下的订单涉及哪些用户:

SELECT DISTINCT user_id FROM orders WHERE status = 1;

如果怀疑界面统计数字不对,先查重复数据,多半能找到原因。

3. 聚合统计:COUNT、SUM、GROUP BY、HAVING 的验证思路

3.1 COUNT 的坑:COUNT(*) 和 COUNT(字段)不一样

聚合统计是测试工程师校验数据的重要手段。最基础的是 COUNT。

SELECT COUNT(*) FROM orders WHERE status = 1; SELECT COUNT(1) FROM orders WHERE status = 1; SELECT COUNT(remark) FROM orders WHERE status = 1;

COUNT(*)统计满足条件的总行数,COUNT(字段)统计该字段不为 NULL 的行数。如果某条记录的 remark 是 NULL,COUNT(remark)就不会把它算进去。平时做总条数校验,用COUNT(*)最稳妥。

去重统计用COUNT(DISTINCT 字段)

SELECT COUNT(DISTINCT user_id) FROM orders WHERE status = 1;

这个值代表有多少个用户下过已支付状态的订单,跟订单总数是两个概念。

3.2 GROUP BY + HAVING:按维度统计,和界面数字对账

GROUP BY 是按一个或多个字段分组,然后对每组做聚合。测试里最典型的场景是:界面按订单状态显示数量,SQL 里也要按状态分组统计。

SELECT status, COUNT(*) AS order_cnt, SUM(amount) AS total_amount FROM orders WHERE create_time >= '2025-01-01' GROUP BY status;

如果结果和界面对不上,说明界面筛选条件或统计逻辑可能有问题。这时候要回头看 WHERE 条件是否一致。

HAVING 用于过滤分组后的结果。它和 WHERE 的区别是:WHERE 在分组之前过滤行,HAVING 在分组之后过滤组。

SELECT user_id, COUNT(*) AS order_cnt FROM orders GROUP BY user_id HAVING COUNT(*) > 5;

这条语句查的是下单超过 5 次的用户,常用于识别高频用户或异常刷单。MySQL 5.7 以上默认开启ONLY_FULL_GROUP_BY,SELECT 后面只能放分组字段和聚合函数,不能随便混入其他字段,否则会报错。

3.3 常用统计场景:平均值、最大最小值

SUM、AVG、MIN、MAX 也很常用。

SELECT COUNT(*) AS total_cnt, SUM(amount) AS total_amount, AVG(amount) AS avg_amount, MAX(amount) AS max_amount, MIN(amount) AS min_amount FROM orders WHERE create_time BETWEEN '2025-01-01' AND '2025-01-31';

对比时要注意 AVG 对 NULL 的处理:AVG(字段) 会忽略 NULL 行。如果字段里有大量 NULL,平均值可能和界面上的算法不一致,需要先确认产品规则。

分组合计加日期格式化,可以统计每天的数据:

SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS day, COUNT(*) AS order_cnt FROM orders WHERE create_time >= '2025-01-01' GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') ORDER BY day;

这种查询在测试报表类功能时很常见。界面展示一张日趋势图,数据库结果就是核对依据。

4. 多表关联:JOIN 怎么查业务关联数据

4.1 INNER JOIN / LEFT JOIN 怎么选

测试环境里的表很少是孤立的。订单表里有 user_id,但用户名在用户表;商品表里只有 category_id,分类名称在分类表。这时候要用 JOIN 把多张表连起来查。

SELECT o.order_no, u.user_name, o.amount FROM orders o INNER JOIN users u ON o.user_id = u.id WHERE o.status = 1;

INNER JOIN 只返回两边都匹配上的行。用户表里找不到的订单,不会出现在结果里。

LEFT JOIN 会保留左表全部数据,右表没有匹配时字段显示为 NULL。

SELECT o.order_no, o.user_id, u.user_name FROM orders o LEFT JOIN users u ON o.user_id = u.id;

如果只关心有订单的用户,用 INNER JOIN;如果想看所有订单,包括那些用户已经被删除的订单,用 LEFT JOIN。

4.2 ON 和 WHERE 的区别

这是 JOIN 查询里最容易踩坑的地方。ON 是关联条件,WHERE 是结果过滤条件,二者执行顺序不一样,尤其在 LEFT JOIN 里差别很大。

-- LEFT JOIN 下把右表条件放 WHERE,可能会丢失左表记录 SELECT o.order_no, o.user_id, u.user_name FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.status = 1;

如果右表 users.status = 1 的过滤写在 WHERE 里,那么用户状态不是 1 的订单会被过滤掉,LEFT JOIN 的效果就变成了 INNER JOIN。正确的做法是把右表过滤条件放进 ON 子句:

SELECT o.order_no, o.user_id, u.user_name FROM orders o LEFT JOIN users u ON o.user_id = u.id AND u.status = 1;

判断标准很简单:如果希望保留左表所有记录,右表条件写在 ON 里;如果就是要过滤掉未匹配的记录,写在 WHERE 里也没问题。

4.3 孤儿数据、笛卡尔积、重复行

测试里有一个很有用的场景:查找没有关联用户的订单,也就是孤儿数据。

SELECT o.* FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.id IS NULL;

返回结果就是 user_id 在用户表里不存在的订单。这类数据通常会影响统计报表,测试时发现数字对不上,可以先查这个。

JOIN 常见的两种问题:

  • 忘记写 ON 或 ON 写错,会产生笛卡尔积,结果行数是两表行数相乘,数量会爆炸式增长。
  • 关联字段不是唯一的,比如客户表和联系方式表一对多关联,JOIN 后订单行数变多,需要去重或改成聚合查询。

所以遇到 JOIN 后结果明显变多时,先检查关联字段是否唯一,再检查有没有漏写条件。

5. 数据准备和变更:INSERT、UPDATE、DELETE 的边界

5.1 INSERT 造数:单条、批量、SELECT 复制

查询手册不能只写 SELECT。测试工程师另一个高频需求是用 INSERT 造数。

单条插入:

INSERT INTO orders (order_no, user_id, amount, status, create_time) VALUES ('TEST20250101001', 1001, 99.00, 1, NOW());

批量插入只要在 VALUES 后面跟多个括号:

INSERT INTO orders (order_no, user_id, amount, status, create_time) VALUES ('TEST20250101002', 1002, 199.00, 1, NOW()), ('TEST20250101003', 1003, 299.00, 1, NOW());

如果要复制一批历史数据到快照表,可以用INSERT INTO ... SELECT

INSERT INTO orders_snapshot (order_no, user_id, amount, status) SELECT order_no, user_id, amount, status FROM orders WHERE create_time >= '2025-01-01';

造数时先看字段约束,比如唯一键、非空字段、默认值。批量造数时不要一次性插几十万条,数据库会扛不住,也容易把测试库搞乱。

5.2 UPDATE 前先 SELECT:影响范围、不加 WHERE 的风险

UPDATE 语法本身不复杂:

UPDATE 表名 SET 字段 = 新值 WHERE 条件;

但这条命令在测试环境里最危险。很多同学写 UPDATE 时不带 WHERE,一执行就把整张表的数据全改了。公共测试库出现这种情况,浪费的是整个测试小组的时间。

我的习惯是:写 UPDATE 之前,先写一条相同 WHERE 条件的 SELECT。

SELECT order_no, status FROM orders WHERE order_no = 'TEST20250101001';

确认这条记录确实存在、确实是想要的目标,再执行:

UPDATE orders SET status = 2 WHERE order_no = 'TEST20250101001';

热搜词里有个“mysql 中 int + 5”,其实就是字段算术运算。常见场景是给用户的积分加 5:

UPDATE points SET bonus = bonus + 5 WHERE user_id = 1001;

注意字段类型。如果 bonus 是 varchar,MySQL 会做隐式转换;转换失败会报错或结果不对。正规做法是把这类字段设计成数值类型,SQL 里直接写算术表达式。

5.3 DELETE 清理和事务回滚

DELETE 比 UPDATE 更要谨慎。

DELETE FROM orders WHERE order_no = 'TEST20250101001';

生产环境不要随便执行 DELETE,测试环境清理数据时也要先确认范围。如果担心删错,可以先开一个事务,删完检查没问题再提交:

START TRANSACTION; SELECT * FROM orders WHERE order_no = 'TEST20250101001'; DELETE FROM orders WHERE order_no = 'TEST20250101001'; -- 确认影响行数正确后 COMMIT; -- 如果发现删错了 -- ROLLBACK;

事务是测试人员保护自己的一层保险。平时练习可以多用START TRANSACTION ... ROLLBACK,验证更新逻辑的同时不污染数据。

TRUNCATE 也可以清空表数据,但不能带 WHERE,是整体清空。用之前一定要确认表名,我见过有人把测试环境的表 TRUNCATE 之后才发现清错库,数据直接没了。

5.4 锁表、长事务、存储过程造数

如果一条 UPDATE 或 DELETE 执行后一直不结束,大概率是锁表了。优先看进程列表:

SHOW PROCESSLIST;

看到有会话长期处于Waiting for table metadata lock或者Updating状态,说明其他事务持有了锁。确认是僵尸会话后,可以在有权限的前提下执行KILL 12345;,其中的 ID 从 PROCESSLIST 结果里看。

还有一个隐蔽问题:测试环境开着长事务不提交。比如你执行了 UPDATE,但一直没 COMMIT,其他连接再改同一行就会一直等待。轻量测试最好事务内操作完就提交或回滚,不要长时间挂着一个事务。

大批量造数时,可以用存储过程或脚本循环插入。比如写一个 WHILE 循环,每 1000 条 COMMIT 一次,避免一次性插入几十万条数据导致锁时间过长。实际项目中我更推荐用接口或脚本分批造数,存储过程适合一次性构造基础数据,不适合频繁变更的测试环境。

6. 常用函数、中文乱码和面试高频点

6.1 字符串、日期、数值函数和字段运算

测试查数时经常需要对结果做格式化。常用函数不需要全背,记住几个高频的就能覆盖大部分场景。

字符串:

  • CONCAT(a, b)拼接字段
  • SUBSTRING(str, start, len)截取字符串
  • LENGTH(str)返回字节长度
  • CHAR_LENGTH(str)返回字符长度,中文场景推荐这个
  • REPLACE(str, old, new)替换内容
  • UPPER(str)/LOWER(str)大小写转换
  • TRIM(str)去掉两端空格

日期:

  • NOW()当前时间
  • CURDATE()当前日期
  • DATE_FORMAT(date, '%Y-%m-%d')格式化日期
  • DATEDIFF(end, start)日期差
  • DATE_ADD(date, INTERVAL 1 DAY)日期加减

数值:

  • ROUND(num, 2)四舍五入
  • CEIL(num)向上取整
  • FLOOR(num)向下取整

使用DATE_FORMAT(create_time, '%Y-%m-%d')时,注意格式符号大小写。%Y是四位年份,%y是两位年份,写错结果会差很多。

6.2 CASE WHEN:把状态数字变成可读结果

测试人员查数据库时,经常看到一堆状态数字,很不利于核对。CASE WHEN 可以把数字翻译成业务名称。

SELECT order_no, CASE WHEN status = 1 THEN '待支付' WHEN status = 2 THEN '已支付' WHEN status = 3 THEN '已取消' ELSE '其他' END AS status_name FROM orders;

它还可以配合 SUM 做条件统计,一次查出多个状态的数量:

SELECT SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS wait_pay_cnt, SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) AS paid_cnt FROM orders;

这种写法比多次查询更高效,结果也更容易和界面数字对账。

6.3 中文乱码和字符集

MySQL 中文乱码,绝大多数是字符集不一致。数据库、表、连接、客户端各有一套编码,任何一层不一致都可能出乱码。

先看表和库的字符集:

SHOW CREATE TABLE orders;

如果表定义里CHARSET=utf8mb4,说明表本身没问题。命令行连接时指定字符集:

mysql -h127.0.0.1 -uroot -p --default-character-set=utf8mb4

排序和统计中文字段时,字符集也会影响结果。测试环境尽量统一使用utf8mb4,它能存储四字节的 Emoji 等内容,适用范围更广。

6.4 面试高频点和学习建议

MySQL 相关面试题里,测试岗位最常问到的有这些:

  • or能不能去重,去重有哪些方式。
  • COUNT(*)COUNT(字段)的区别。
  • GROUP BYHAVING的用法。
  • LIMIT分页语法。
  • UPDATE不加 WHERE 会发生什么。
  • 如何查找并删除重复数据,保留一条。
  • INNER JOINLEFT JOIN的区别。
  • 如何查每个用户最新的一笔订单。

最后这个问题需要窗口函数,MySQL 8.0 及以上版本可以这样写:

SELECT order_no, user_id, create_time FROM ( SELECT order_no, user_id, create_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY create_time DESC) AS rn FROM orders ) t WHERE rn = 1;

PARTITION BY user_id表示按用户分组,ORDER BY create_time DESC表示组内按时间倒序,rn = 1取每组第一条。老版本 MySQL 不支持窗口函数,就用临时表或子查询绕一下。

学习建议其实很简单:本地装一个 MySQL,准备一套用户表、订单表、商品表,把平时测试项目的常见问题转换成 SQL 练习。先练单表查询,再练 JOIN 和聚合,最后碰 UPDATE、DELETE。不要背命令,背命令解决不了“界面数字对不上”这类实际问题。

最后留一句我自己的经验:测试工程师写 SQL,稳定比花哨重要。真正常用的命令不超过几十条,但每条都要知道它影响什么、会返回什么、在什么情况下不能随便用。能把造数、校验、定位、清理这四件事做到不出错,这份查询能力就已经很值钱了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 15:17:45

数学建模竞赛D题实战:从Python预测到优化模型的完整解题路径

1. 项目概述&#xff1a;从赛题到解题的实战路径又到了一年一度的高教社杯全国大学生数学建模竞赛&#xff08;以下简称“国赛”&#xff09;的备战季。对于很多参赛队伍&#xff0c;尤其是第一次接触建模的同学来说&#xff0c;拿到赛题后最头疼的往往不是某个具体的算法&…

作者头像 李华
网站建设 2026/9/2 5:27:31

基于Python+SUMO+DQN的交通信号灯智能调控实战

简介&#xff1a;交通信号控制是城市智能交通系统的核心环节&#xff0c;其本质是面向离散动作、局部可观测状态的实时决策问题。原理上需兼顾状态建模精度、动作物理约束与强化学习算法收敛稳定性&#xff1b;技术价值体现在将AI决策从仿真环境可靠迁移至真实路口&#xff0c;…

作者头像 李华
网站建设 2026/9/10 15:17:39

Less工程化实战:变量混合器与样式分层构建可维护前端样式架构

1. 项目概述&#xff1a;从样式混乱到工程化秩序如果你接手过一个老项目&#xff0c;打开它的CSS文件夹&#xff0c;看到的是几十个、上百个以“page1.css”、“style_v2_final.css”命名的文件&#xff0c;变量颜色散落在各个角落&#xff0c;一个按钮的样式在五个地方被重复定…

作者头像 李华
网站建设 2026/8/31 7:46:09

15个Agent实战项目清单:从Prompt工程到企业级部署全攻略

做了一段时间 Agent 开发&#xff0c;又花了大量时间把市面上主流的 Agent 开源项目和课程翻了一遍&#xff0c;一个最直接的感受是&#xff1a; Agent 开发的学习资料不缺&#xff0c;缺的是能让人按顺序练完、练完就能写进简历、贴近真实业务需求的项目清单 。 很多人一上…

作者头像 李华
网站建设 2026/9/3 8:46:49

AI开发环境搭建指南:Miniconda与虚拟环境管理实战

1. 项目概述&#xff1a;为什么我们需要一个“工具箱”&#xff1f;刚入行那会儿&#xff0c;我经常被一个看似简单的问题卡住&#xff1a;环境崩了。可能只是想在已有的项目里加个新库&#xff0c;结果 pip install 一通操作后&#xff0c;整个 Python 解释器都变得“六亲不认…

作者头像 李华
网站建设 2026/8/31 9:29:55

C++模板编程:从泛型思想到STL实现的核心技术

1. 从“重复造轮子”到“一劳永逸”&#xff1a;为什么我们需要模板&#xff1f;如果你写过一段时间的C&#xff0c;尤其是写过一些需要处理多种数据类型的函数或类&#xff0c;你大概率经历过这种痛苦&#xff1a;为了给int、double、string分别实现一个功能完全相同的swap函数…

作者头像 李华