news 2026/10/2 14:44:09

MySQL、Oracle、SQLServer语法差异与迁移实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL、Oracle、SQLServer语法差异与迁移实战指南

1. 数据类型与字符串处理:迁移时最先崩的地方

干这行越久越觉得,MySQL、Oracle、SQLServer 的语法区别就像三个方言极其浓厚的地区,明明都是说“中国话”,可一到具体表达就谁也不服谁。很多朋友费劲装好 MySQL 或 SQLServer,还没高兴两天,就把一套脚本从 Oracle 搬过来,结果从数据类型开始一路报错。其实这第一步的分岔,就发生在字符类型和字符串处理上。

1.1 字符类型:varchar、varchar2 和 nvarchar 不是一回事

先看三家的字符类型命名。MySQL 最常用的是varchar,Oracle 是varchar2,SQLServer 是varchar和nvarchar。光看名字很容易以为它们差不多,但实际行为差别很大。

MySQL 的varchar(n)里的 n 是字符数,不是字节数。比如varchar(50)能存 50 个汉字。但注意它有一个行大小限制,一般情况下varchar的最大长度受限于65535字节,如果表里还有别的字段,实际能用的更少。在 utf8mb4 字符集下,一个汉字占 4 字节,所以varchar(255)在极端情况下就可能超限。

Oracle 的varchar2(n)就比较“拧”,这里的 n 默认是字节数。也就是说varchar2(10)在中文库里可能只能存 3 个汉字。Oracle 官方一直强调要用字符集语义去定义字段,但我见过太多老系统直接按字节定义,导致中文丢数据。而且 Oracle 的varchar2必须显式写长度,不能像 MySQL 那样给个默认值。

SQLServer 又把事情分成两半:varchar是非 Unicode 字符串,nvarchar是 Unicode 字符串。如果字段里有中文、韩文、日语这些,最好用nvarchar,否则容易出现乱码。插入中文时,SQLServer 建议加N前缀,比如N'张三',这个细节很多从 Oracle 转过来的人根本不习惯。

对比项MySQLOracleSQLServer
常用类型varchar(n)varchar2(n)varchar/nvarchar
n 的含义字符数字节数(默认)字符数
中文建议utf8mb4 下直接存用 varchar2(n char) 或加大长度用 nvarchar + N 前缀
空字符串 ''合法,区别于 NULL等于 NULL合法,区别于 NULL

这里藏着 Oracle 的一个大坑:''会被当成NULL,所以很多从 Oracle 迁出来的表里,明明看着是空字符串,实际存的是 NULL。写判断语句时,如果只判断name = '',可能查出来的数量对不上。

字符串拼接同样不统一。MySQL 里的+是算术加号,想拼接必须用CONCAT函数。Oracle 跟标准 SQL 走,用||。SQLServer 则用+,而且它很“聪明”,会自动把数字转成字符串然后拼接。

举一个真实的现场:有个同事从 SQLServer 迁到 MySQL,原SQL是SELECT id + '-' + name,结果 MySQL 把+当成加法,整个表达式直接变成数学运算,输出一堆 0 和 1。改成CONCAT(id, '-', name)才正常。所以每到新环境,先确认拼接符号,就能避开一大半低级故障。

1.2 字符串转数字:CAST、CONVERT 与 TO_NUMBER

热搜词里“sqlserver 字符串转数字”反复出现,说明这是高频需求。三家的实现各有各的名字:

  • MySQL:CAST('123' AS SIGNED)或CONVERT('123', SIGNED),也支持直接用字段 + 0做隐式转换。
  • Oracle:TO_NUMBER('123'),字符串里带货币符号、千分位时需要用格式模板,比如TO_NUMBER('1,200', '9,999')。
  • SQLServer:CAST('123' AS INT)或CONVERT(INT, '123')。想防止异常直接报错,可以用TRY_CAST和TRY_CONVERT,转换失败时返回 NULL。

注意一个隐藏问题:如果字段是varchar类型存的是'10'和'9',直接比较会按字符顺序排,'10'会排在'9'前面。解决办法就是显式转成数字再排序。MySQL 里常见写法是ORDER BY CAST(col AS UNSIGNED)或者是ORDER BY col + 0,Oracle 写ORDER BY TO_NUMBER(col),SQLServer 写ORDER BY CAST(col AS INT)。

这类转换还要考虑 NULL。三家的空值函数分别是:

  • MySQL:IFNULL(a, b)和COALESCE(a, b)
  • Oracle:NVL(a, b)和COALESCE(a, b)
  • SQLServer:ISNULL(a, b)和COALESCE(a, b)

它们都能在字段为 NULL 时返回替代值。尽量优先用COALESCE,因为它是 SQL 标准函数,跨数据库差异最小。不过NVL和IFNULL在各自生态里已经用得铺天盖地,迁移时经常要整体替换。

2. 查询写法差异:分页、排序与条件逻辑

查询语句是日常动用最频繁的部分,也是语法区别最直观的体现。同样一个“按 id 排序后取第 21 到 30 条”的需求,三个数据库的写法可能完全不是一套逻辑。

2.1 分页:LIMIT、ROWNUM 和 OFFSET FETCH

MySQL 最简单,直接LIMIT:

SELECT * FROM user ORDER BY id LIMIT 20, 10; -- 或者 SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20;

这里20是跳过多少条,第二段是取多少条。MySQL 5.7 及之前的写法很容易把顺序记反,写成了LIMIT 10, 20就变成跳过 10 条取 20 条。

Oracle 传统写法要绕一个弯。因为ROWNUM是查询结果生成时顺手赋的行号,而不是先排序后赋号。想取排序后的区间,必须三层嵌套:

SELECT * FROM ( SELECT t.*, ROWNUM rn FROM ( SELECT * FROM user ORDER BY id ) t WHERE ROWNUM <= 30 ) WHERE rn > 20;

这个写法在 Oracle 11g 及以前尤其常见。如果少了最内层的排序,取到的就可能不是按 id 排序后的第 21 到 30 条。Oracle 12c 开始推出了OFFSET ... FETCH,跟 SQLServer 的写法接近:

SELECT * FROM user ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

SQLServer 2008 及更早的年代,没有OFFSET,最常用ROW_NUMBER():

SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) rn FROM user ) t WHERE rn BETWEEN 21 AND 30;

SQLServer 2012 之后才支持OFFSET...FETCH:

SELECT * FROM user ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

注意 SQLServer 的OFFSET一定要求有ORDER BY,否则直接报错。取单条记录时,MySQL 用LIMIT 1,Oracle 用ROWNUM <= 1或者新版的FETCH FIRST 1 ROWS ONLY,SQLServer 用TOP 1。很多人不知道 Oracle 老版本里不能直接写WHERE ROWNUM = 1之外的范围,因为ROWNUM > 1永远不成立,这是我被问过最多的问题之一。

分页性能上的区别也值得说。MySQL 深分页时会越翻越慢,比如LIMIT 1000000, 10,要先扫描前一百万行再丢弃,通常的优化方案是先查主键,再用主键关联取值。Oracle 因为ROWNUM在生成时就会终止扫描,数据读取量相对可控。SQLServer 的ROW_NUMBER()需要全局排序,大数据量下内存和临时库压力大,新的OFFSET方式同样有深翻页问题,Keyset Paging 是更好的办法。

2.2 排序空值与条件逻辑:同样一个ORDER BY,结果不一样

排序时遇到 NULL,三家的默认行为不同,这是特别容易踩的乱子。

数据库升序默认位置如何控制
MySQLNULL 最前ORDER BY IFNULL(col, 0)或ORDER BY col IS NULL, col
OracleNULL 最后ORDER BY col NULLS LAST
SQLServerNULL 最前ORDER BY CASE WHEN col IS NULL THEN 1 ELSE 0 END, col

比如排行榜字段score是空的,MySQL 和 SQLServer 默认把 NULL 排在最前面,Oracle 默认把 NULL 排在最后面。如果不统一规则,同一个报表在三个库跑出来,排序结果完全不一样。我一般建议在 SQL 里显式写明空值处理,不要依赖默认行为。

条件逻辑也有差异。MySQL 提供了IF(expr, val1, val2)函数,Oracle 喜欢用DECODE(expr, val1, result1, ... DEFAULT),SQLServer 2012 以后也提供了IIF。不过它们都属于各自扩展,如果想保持兼容,老老实实写标准CASE WHEN最稳妥。

布尔类型的表达同样奇怪。MySQL 里BOOLEAN本质是TINYINT(1),可以写WHERE flag。Oracle 没有真正的布尔类型,通常用NUMBER(1)或者CHAR(1),所以必须写成WHERE flag = 1。SQLServer 有BIT类型,但也不能直接写WHERE flag,要写WHERE flag = 1。从 MySQL 迁出去的人最容易在这里碰运气。

模糊查询的通配符也有差别。三家的%和_都一样,但 SQLServer 额外支持方括号[0-9]这类字符范围匹配。MySQL 的传统LIKE不支持这种写法,可以用REGEXP正则替代。涉及到_本身要匹配时,记得用ESCAPE指定转义符,不然下划线会被当成通配符。

3. 函数家族:日期时间与字符串函数的“双胞胎不同命”

函数是语法差异的另一大集中地。尤其日期函数,因为数据库的存储格式、时区策略、语言环境不同,导致写法五花八门。

3.1 当前时间和日期格式化:NOW、SYSDATE 与 GETDATE

获取当前时间,MySQL 最常用NOW(),Oracle 使用SYSDATE或SYSTIMESTAMP,SQLServer 使用GETDATE()。格式化日期就更“散装”了:

MySQL 写:

SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s');

Oracle 写:

SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;

SQLServer 写:

SELECT FORMAT(GETDATE(), 'yyyy-MM-dd HH:mm:ss'); -- 或者 SELECT CONVERT(VARCHAR(19), GETDATE(), 120);

Oracle 的TO_CHAR用的是HH24、MI、SS这套格式符,MySQL 则用%H、%i、%s。SQLServer 除了FORMAT函数,老项目里更喜欢CONVERT配合 style 参数,比如120就代表yyyy-mm-dd hh:mi:ss。这些 format 代码各不相同,迁移时改到怀疑人生。

日期运算更是经典。Oracle 里日期直接加减数字,SYSDATE + 1就是明天同一时间。MySQL 需要使用INTERVAL,写成DATE_ADD(NOW(), INTERVAL 1 DAY)。SQLServer 用DATEADD(day, 1, GETDATE())。取两个日期之间的天数,MySQL 用DATEDIFF(a, b),Oracle 直接a - b得到带小数点的天数,SQLServer 用DATEDIFF(day, a, b)。

日期类型本身也要注意。MySQL 的DATE只有日期,DATETIME包含时分秒,TIMESTAMP还受时区影响。Oracle 的DATE其实包含时分秒,很多人以为是纯日期,结果 JDBC 读取时莫名其妙多了 00:00:00。SQLServer 的DATE是新版的纯日期,老式的DATETIME范围到 2079 年,而datetime2范围更广且精度更高。

3.2 字符串函数:LENGTH 和 LEN,SUBSTR 和 SUBSTRING

字符串函数最常见的就是取长度、截取、找位置、补位和去空格。这些函数名字相近,但行为和返回值常常不一样。

取长度:MySQL 的LENGTH返回字节数,CHAR_LENGTH返回字符数;Oracle 的LENGTH返回字符数,LENGTHB返回字节数;SQLServer 的LEN返回字符数但会自动去掉尾部空格,DATALENGTH返回字节数。拿“中文”这两个字做测试,MySQL 在 utf8mb4 下LENGTH可能是 8,CHAR_LENGTH是 2。不看清楚就会得到“神奇”结果。

截取字符串:MySQL 和 Oracle 都习惯用SUBSTRING/SUBSTR,Oracle 更常见的是SUBSTR(str, start, len),SQLServer 同样是SUBSTRING(str, start, len)。注意这三个数据库的截取都是从 1 开始计数,完全符合日常直觉,但就是有人总认为是 0 开始。

查找字符位置:MySQL 用LOCATE或INSTR,Oracle 用INSTR,SQLServer 用CHARINDEX。返回值都是位置数字,但参数顺序不一样。比如 SQLServer 是CHARINDEX(子串, 字符串),Oracle 的INSTR(字符串, 子串)刚好反过来,迁移时特别容易把参数写反。

补位函数:MySQL 有LPAD和RPAD,Oracle 也有,SQLServer 原生没有,需要用REPLICATE('0', 5 - LEN(col)) + col或者RIGHT(REPLICATE('0', 5) + col, 5)这种方式模拟。去空格三家里LTRIM、RTRIM都有,但 SQLServer 是从 2017 版才提供了标准TRIM,老版本只能LTRIM(RTRIM(...)),而 MySQL 和 Oracle 直接用TRIM。

4. 存储过程、事务与元数据查询的进阶差异

函数和查询只是开胃菜,真正让迁移工作量直线上升的是存储过程和事务体系。如果业务逻辑大量写在数据库端,换库基本等于重写一部分后端代码。

4.1 存储过程的框架差异:分隔符、参数模式与异常处理

先看一段最简单的存储过程,作用是按 id 查名字,并返回给外部变量。

MySQL 写法:

DELIMITER // CREATE PROCEDURE get_user(IN uid INT, OUT uname VARCHAR(50)) BEGIN SELECT name INTO uname FROM user WHERE id = uid; END// DELIMITER ;

Oracle 写法:

CREATE OR REPLACE PROCEDURE get_user(uid IN NUMBER, uname OUT VARCHAR2) AS BEGIN SELECT name INTO uname FROM user WHERE id = uid; END;

SQLServer 写法:

CREATE PROCEDURE get_user @uid INT, @uname NVARCHAR(50) OUTPUT AS BEGIN SELECT @uname = name FROM [user] WHERE id = @uid; END;

看起来结构相似,但细节差异不少。MySQL 必须用DELIMITER改分隔符,避免存储过程内部的;把整段语句拆碎。Oracle 没有这个苦恼,但它要求IN和OUT要明确写出,而且没有INOUT,用的是IN OUT。SQLServer 的参数名全都要带@,输出参数还要标OUTPUT。

异常处理也各成一派。MySQL 的存储过程里写DECLARE EXIT HANDLER FOR SQLEXCEPTION,Oracle 用异常块:

BEGIN ... EXCEPTION WHEN OTHERS THEN ... END;

SQLServer 则是BEGIN TRY ... BEGIN CATCH ... END。游标部分同样不统一,Oracle 有隐式游标和FOR ... IN写法,SQLServer 要先DECLARE CURSOR再OPEN再FETCH,MySQL 也类似。这些都导致存储过程迁移不仅只是替换函数名,而是要重新梳理控制流。

动态 SQL 也是一个大项。Oracle 常用EXECUTE IMMEDIATE,SQLServer 常用EXEC sp_executesql,MySQL 用PREPARE和EXECUTE。参数绑定方式,MySQL 用?,Oracle 用:name,SQLServer 用@name,代码改造量肉眼可见。

4.2 事务控制与锁行为:AUTOCOMMIT 的坑

事务是三家中差异容易隐藏得很深的部分。

MySQL 默认autocommit = 1,执行一条语句就自动提交。想开启一个事务要写START TRANSACTION或BEGIN,并且要注意 DDL 语句会隐式提交当前事务。Oracle 的事务模式不太一样,它没有一个显式的“开启事务”动作,从第一条 SQL 开始就进入了事务,直到执行COMMIT或ROLLBACK。SQLServer 默认也是自动提交模式,显式使用BEGIN TRANSACTION。

因为默认提交策略不同,从 Oracle 迁到 MySQL 的代码里,如果原封不动图省事省掉显式事务开头,可能出现想回滚却已经提交的情况。反过来,从 MySQL 迁到 Oracle,如果仍然每个操作后都COMMIT,虽然不会出错,但事务粒度被拆得很碎,性能会差。

锁行为差异就更大了。SQLServer 有一种常见的默认读行为是加共享锁,很多人处理大表查询时会写WITH (NOLOCK)提示,允许脏读。Oracle 的多版本并发控制使普通查询不阻塞写、写不阻塞读,所以根本没有必要用那种WITH (NOLOCK)提示。MySQL InnoDB 也是 MVCC,普通 SELECT 在可重复读下不会加锁,但如果要锁定某一行更新,三家的写法都需要考虑SELECT ... FOR UPDATE或应用层控制。

隔离级别默认值同样要留意:MySQL InnoDB 默认为REPEATABLE READ,Oracle 默认为READ COMMITTED,SQLServer 默认为READ COMMITTED。同一个事务内两次查询,MySQL 可能读到一致快照,Oracle 每次都会读最新已提交数据。这个差异会导致相同的业务逻辑出现不一样的结果,特别是在统计报表和状态判断场景。

4.3 元数据与系统表:查表结构的方式完全不同

开发中经常要判断表是否存在、查某张表的字段信息。三个数据库的系统表命名天差地别。

MySQL 可以用SHOW TABLES、DESC 表名、SHOW CREATE TABLE,也支持information_schema.COLUMNS。Oracle 强烈依赖数据字典视图,比如USER_TABLES、ALL_TABLES、USER_TAB_COLUMNS,在 SQL*Plus 里可以用DESCRIBE查结构。SQLServer 常见的是sys.tables、sys.columns,或者系统存储过程sp_help、sp_columns。

如果想写一套跨库查询列信息的逻辑,并不容易。INFORMATION_SCHEMA.COLUMNS在 MySQL 和 SQLServer 里都有,但 Oracle 并没有完整提供这套视图,更多要靠ALL_TAB_COLUMNS。所以迁移工具大多会针对每个数据库写单独的数据字典查询,而不是指望 SQL 完全兼容。

删除表也体现风格。MySQL 支持DROP TABLE IF EXISTS test_table,SQLServer 2016 开始也支持同样写法,但老版本要写成:

IF OBJECT_ID('test_table', 'U') IS NOT NULL DROP TABLE test_table;

Oracle 里没有IF EXISTS,只能用 PL/SQL 块先判断:

BEGIN EXECUTE IMMEDIATE 'DROP TABLE test_table'; EXCEPTION WHEN OTHERS THEN NULL; END;

对象名的处理方式也需要注意。Oracle 未加双引号的表名会自动转成大写,MySQL 的表名大小写敏感度取决于lower_case_table_names参数,SQLServer 则取决于排序规则。同一套建表脚本,可能在 Oracle 里生成USER_TABLE,在 MySQL 里写的是user_table,导致 JDBC 查询时找不到对象。

5. 迁移实战经验与快速记忆法

前面把常用差异拆了一遍,可能看着有些碎。我在实际迁移项目里通常不会同时处理所有差异,而是先做一轮“方言映射”,把高风险点找出来,再逐个击破。这里分享几个记忆方法和实际踩坑现场。

5.1 三个高频踩坑案例

第一个是字符串转数字。某个计数接口,原来在 SQLServer 里用ISNULL(SUM(CONVERT(INT, amount)), 0)统计金额,迁移到 Oracle 后改成NVL(SUM(TO_NUMBER(amount)), 0),看起来没问题。但实际amount列里有空字符串,Oracle 的TO_NUMBER遇到空字符串会直接报ORA-01722。因为 Oracle 把''当作 NULL,而 SQLServer 的空字符串在转换时没这么严格。最后用NVL(TO_NUMBER(NULLIF(TRIM(amount), '')), 0)才绕过去。

第二个是分页慢。一个订单列表从 Oracle 迁到 MySQL,开发同学直接用LIMIT 600000, 20,结果生产环境一次查询耗掉两秒多。后来改成先取出满足条件的主键 id,再关联查询:

SELECT * FROM orders WHERE id IN ( SELECT id FROM orders ORDER BY create_time DESC LIMIT 600000, 20 );

虽然写法一样笨,但走覆盖索引后速度提升明显。这个经验不一定对所有 MySQL 版本都有效,但遇到深分页时值得先试。

第三个是空值排序。一个 APP 的商品列表要求“价格相同的,优先展示库存高的”,库存字段正好有 NULL。MySQL 里默认 NULL 排最前,结果一批没有库存的商品反而全部跑到前面。最后统一转成 0:

ORDER BY price, IFNULL(stock, 0) DESC

如果是 Oracle,就要写成NVL(stock, 0);如果是 SQLServer,则写成ISNULL(stock, 0)。问题很小,但排查起来往往要耗半天。

5.2 快速记忆法:一个映射表吃透大半

记住下面这张高频映射表,能应对大部分日常开发:

功能MySQLOracleSQLServer
字符串拼接CONCAT||+
空值替换IFNULLNVLISNULL
取字符串长度CHAR_LENGTHLENGTHLEN
截取子串SUBSTRINGSUBSTRSUBSTRING
当前时间NOW()SYSDATEGETDATE()
日期加一天DATE_ADD(... INTERVAL 1 DAY)SYSDATE + 1DATEADD(day, 1, GETDATE())
分页LIMITROWNUM / FETCHOFFSET FETCH / ROW_NUMBER
字符串转数字CASTTO_NUMBERCAST / CONVERT
表结构查询SHOW CREATE TABLE / DESCUSER_TAB_COLUMNSsp_help / sys.columns

我个人接手跨数据库项目时,第一步不是去翻手册,而是先根据数据库版本把这张表补齐,再扫一遍 SQL 脚本里的关键字,优先处理LIMIT、ROWNUM、NVL、SYSDATE、GETDATE这类方言特征明显的词。等这些高危点清完,再处理编码和排序规则问题,整体迁移就顺利很多。

语法差异再多,背后其实是三个数据库的设计哲学不一样:MySQL 追求轻量和易用,Oracle 强调严谨和稳定,SQLServer 则是微软生态下的强力整合。理解了它们的习惯,很多“为什么这样写”的问题就迎刃而解。我之前帮客户做过一次从 Oracle 到 MySQL 的完整迁移,前后花了两周梳理语法,真正改代码的时间反而只有三天。希望大家遇到这种活儿时,先把对照表和踩坑笔记准备好,比我当时少走弯路。

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

OpenHarmony实战:React Native工具模块开发与排错指南

前阵子把手上一个跑在OpenHarmony真机上的英雄联盟助手App的实用工具模块整体重构了一遍&#xff0c;从RN桥接电话能力、FTP资源同步到HDI硬件接口调用&#xff0c;每个环节都踩了不少坑。先说结论&#xff1a;React Native for OpenHarmony这套组合拳完全能打&#xff0c;但前…

作者头像 李华
网站建设 2026/10/2 14:42:54

智能视频行为分析系统落地实战:从需求拆解到分布式部署全复盘

做了近十年的安防视频项目&#xff0c;这两年最常听到的需求已经从“帮我装摄像头”变成了“让摄像头自己会看”。我现在做的这套智能视频行为分析系统&#xff0c;落地在一个精密制造车间&#xff0c;客户给的要求非常具体&#xff1a;车间里有人跌倒、快速奔跑、翻越护栏、异…

作者头像 李华
网站建设 2026/10/2 14:42:49

Simulink直流无刷电机仿真模型搭建指南:5分钟快速上手

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

作者头像 李华
网站建设 2026/10/2 14:42:05

私有环境RAG知识库搭建实战:从文档切分到微信钉钉接入

1. 为什么要在私有环境里搭一套 RAG 知识库1.1 从“模型很聪明”到“模型懂我们公司”的落差大模型刚火那阵子&#xff0c;我身边不少朋友的第一反应都是&#xff1a;这东西这么能聊&#xff0c;直接拿它当客服、当内部助手不就完了&#xff1f;真上手用一段时间就会发现&#…

作者头像 李华
网站建设 2026/10/2 14:42:05

电机控制框架选型实战:五套架构优缺点与工程决策指南

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

作者头像 李华
网站建设 2026/10/2 14:41:35

C++单元测试中的Mock实战:用gMock隔离依赖与提升可测试性

从给一个“下载器”类写单元测试开始说起吧。这类类对象往往依赖网络库、磁盘读写、甚至是系统时间&#xff0c;如果你真的在单测里发起HTTP请求&#xff0c;那测试就变成了“原谅我不厚道地笑了”现场——CI不稳定、跑得慢、失败了还不知道是代码错了还是网络抽风。这个场景正…

作者头像 李华