news 2026/9/29 9:09:31

软件测试必备:每天5分钟掌握SQL查询与INSERT数据操作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
软件测试必备:每天5分钟掌握SQL查询与INSERT数据操作

做软件测试,尤其是功能测试和接口测试的,早晚会遇到一个躲不开的场面:你刚提交了一个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没有想象中那么神秘,它就是你和数据之间的对话,而对话的第一步,就是敢开口问一句:“喂,表里到底有什么?”

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

Ubuntu下kill进程全解析:从信号机制到kill -9的正确使用姿势

最近后台好几个读者留言问同一个问题:在 Ubuntu 上跑着一个卡死的程序,前台 CtrlC 不起作用,直接关终端又怕把数据搞坏,到底该用 kill 那个参数?有人张口就是 kill -9 无脑强杀,有人连 kill 和 pkill 的区别…

作者头像 李华
网站建设 2026/9/29 9:07:39

工业控制器三合一:PLC、HMI与边缘AI融合方案解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 9:03:56

Windows下C++单线程多端口select模型实战与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 9:02:19

仿水印相机样式:Canvas 图片水印合成与定位时间字段设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华