news 2026/10/6 13:26:30

SQL Server与MySQL语法差异对照:从连接配置到迁移踩坑全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQL Server与MySQL语法差异对照:从连接配置到迁移踩坑全指南

干了这么多年数据库开发,SQL Server 和 MySQL 我几乎每天都在切着用。最烦的不是 SQL 写不出来,而是同一个思路在两个库里的写法看起来差不多,跑起来却一个对、一个错,查半天才发现是方言差异。这次我把平时自己备忘的那些点重新整理了一遍,从安装连接、查询语法、表结构、事务锁,到存储过程和迁移踩坑,按“自用”的标准写成这篇东西,顺便分享给需要在两个库之间来回切换的朋友。不想讲太深的存储引擎原理,重点就是“怎么写才对、为什么这么写、出错了怎么查”。

1. 装环境、选工具:两边第一印象就完全不同

1.1 安装和认证那些坑

第一次装 SQL Server 的人,很容易被它的安装流程搞懵。虽然 2016、2017、2019、2022 基本都是下一步下一步,但经常在最后一步报“无法找到数据库引擎启动句柄”。我遇到过很多次,原因其实就三类:一是旧实例残留,安装程序检测到原来的 SQL Server 服务还挂在那,但服务又启动不了;二是防火墙把 1433 端口挡了,服务起没起来你根本不知道;三是安装时选了默认实例,但机器上已经有同名实例,服务名冲突。我自己的排查顺序是先打开services.msc看有没有SQL Server (MSSQLSERVER)服务,接着用管理员身份跑一下net start mssqlserver,再去看安装目录下的ERRORLOG文件。如果日志里提示端口被占用,就改端口或者把旧实例卸干净。实在不行,用安装中心里的“修复”功能重来一遍,多数能救回来。

MySQL 这边安装通常顺很多,但 8.0 的认证插件caching_sha2_password是个经典坑。你装完 8.0,用老版本的 JDBC 驱动或者旧客户端一连接,直接报 SSL 连接错误,或者提示Public Key Retrieval is not allowed。这并不是网络问题,是驱动不支持新认证方式。解决起来两个思路:一是把 JDBC URL 加上allowPublicKeyRetrieval=true&useSSL=false,二是在 MySQL 里把 root 用户改回mysql_native_password。对我这种要兼容老项目的人来说,第二种方法更省心,但安全等级确实低一些,自己评估。

要是还在用 5.7 的同学,安装相对宽松,默认认证就是mysql_native_password,这也是很多生产环境到现在还没升 8.0 的原因。另外,MySQL 装完之后去my.ini改basedir、datadir、port这些基础配置是常规操作,而 SQL Server 一般不需要手动改配置文件,靠“SQL Server 配置管理器”来控制端口、服务、网络协议。两边管理思路不一样,别用习惯硬套。

1.2 图形化工具与连接串差异

工具选择上,SQL Server 官方提供 SSMS,功能全、免费,缺点是启动慢、吃内存。MySQL 官方有 MySQL Workbench,能用,但交互体验一般。如果你经常两边切,我建议直接上 DBeaver 或者 Navicat,一套工具通吃所有数据库,避免装两个客户端、记两套快捷键。命令行方面,SQL Server 的sqlcmd和 MySQL 的mysql客户端都存在,但语法有点差别,日常调试小数据量还行,复杂报表我还是喜欢图形化查。

连接串是第一个真正体现差异的地方。SQL Server 默认端口是 1433,实例名可以跟在服务器名后面,比如localhost\\SQLEXPRESS;MySQL 默认端口是 3306,没有实例名的概念,只有host和port。JDBC 连接串对比如下:

数据库JDBC 连接串示例
SQL Serverjdbc:sqlserver://localhost:1433;databaseName=testdb;user=sa;password=xxx
MySQLjdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=Asia/Shanghai

.NET 环境里,SQL Server 用SqlConnection,连接串通常是Server=localhost;Database=testdb;User Id=sa;Password=xxx;,MySQL 用MySqlConnection,连接串是Server=localhost;Database=testdb;Uid=root;Pwd=xxx;。上面这些差异虽然小,但项目里一配错就是半小时起步的排查时间,我建议直接存到一个常用配置清单里,别靠脑子记。

还有一个很容易被忽略的点:MySQL 的连接用户名是带 host 的,'root'@'localhost'和'root'@'%'是两个账号。你在本机能连、服务器上连不上,很多时候不是密码错,是没给远程 host 授权。SQL Server 没有这套概念,登录名和服务器地址是分开的。第一次从 MySQL 转过来的朋友,遇到“能连本地连不了远程”时,优先往这个方向想。

2. SQL 写法差异:CRUD 和查询里的“同理但不同法”

2.1 查询语句的骨架差异:终止符、别名、多表更新

最直观的差异是语句结束符和批处理分隔符。SQL Server 的 SSMS 里常用GO作为批处理分隔,MySQL 里没有GO,默认用分号;作为语句结束。还有一个细节,SQL Server 的GO并不是 T-SQL 语句,它只是客户端工具识别的一个批处理标记。如果你拿一个带GO的脚本去跑,比如在代码里用 SqlCommand 执行,直接报语法错误。反过来,MySQL 的存储过程定义里需要自己处理DELIMITER $$,SQL Server 完全不需要,这个我们后面讲存储过程时再展开。

大小写敏感这块,也要格外小心。默认情况下,SQL Server 的表名、列名不区分大小写,关键是数据库的排序规则(Collation);而 MySQL 在 Linux 上,数据库名和表名默认区分大小写,Windows 上又基本不区分。我有一阵子把 Linux 上开发好的建表脚本直接拿到 Windows 环境跑,结果一切正常,反过来又出问题,就是大小写导致的。所以迁移时先确认lower_case_table_names这个系统参数是什么值。

多表 UPDATE 和 DELETE 的语法差别比较隐蔽。SQL Server 的 UPDATE 多表联更,是UPDATE a SET ... FROM 主表 a JOIN 关联表 b ON ...,MySQL 则习惯把 JOIN 放在 UPDATE 和 SET 之间,写成UPDATE 主表 a JOIN 关联表 b ON ... SET ...。DELETE 也一样,SQL Server 里DELETE a FROM ...是合法的,MySQL 里DELETE a FROM ...也支持,但如果你不带别名直接写DELETE FROM 主表 WHERE EXISTS (...),在两边都能跑通。我的建议是:多表删除更新统一写子查询 + EXISTS,尽量少用连接语法,这样移植性最好。

2.2 字符串处理:拼接、空值、转数字

字符串拼接是我踩过最多坑的地方。SQL Server 里'abc' + 'def'直接拼,MySQL 里+在字符串上下文里会当数字运算,得用CONCAT('abc', 'def')。更麻烦的是拼接时遇到 NULL:SQL Server 里'a' + NULL结果是NULL,MySQL 的CONCAT('a', NULL)结果也是NULL,但如果用CONCAT_WS,它会跳过 NULL。我一般在需要拼接列表字段时,先用ISNULL或者IFNULL把 NULL 处理掉,再拼,避免结果整列为空。

字符串转数字也是高频需求。SQL Server 最简单的是CAST('123' AS INT)或者CONVERT(INT, '123'),两者都能用。MySQL 同样有CAST('123' AS SIGNED),也有CONVERT('123', SIGNED)这种写法。但这里有个坑:MySQL 的CONVERT有两种完全不同的用法,一个是转字符集,比如CONVERT(name USING utf8mb4),一个是转数据类型,比如CONVERT('123', SIGNED),我第一次用的时候把参数顺序写反,直接报错。另外,SQL Server 对字符串转数字的容忍度很低,'12a'直接转会报错;MySQL 的隐式转换相对宽松,'12a'转出来往往是 12,但这反而容易掩盖数据问题,我建议两边转数字都尽量先用正则或者TRY_CAST(SQL Server 2012+)做校验。

空值处理函数也要区分:SQL Server 用ISNULL(a, b),MySQL 用IFNULL(a, b),两边共同支持COALESCE(a, b, c)。我自己的习惯是统一用COALESCE,这样在两边都不用改。别忘了NULLIF(a, b)两边都有,做除法防除零很有用,比如SELECT a / NULLIF(b, 0) FROM t。

2.3 分页与排名:OFFSET FETCH 与 LIMIT

分页可以说是切换数据库时最容易写错的地方。SQL Server 2012 之前的版本没有 OFFSET,老项目里全是ROW_NUMBER() OVER (ORDER BY id)套一层子查询,又长又绕。2012 之后终于支持标准写法:

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

MySQL 简单直接:

SELECT * FROM t ORDER BY id LIMIT 20, 10;

两个库现在都支持窗口函数,所以ROW_NUMBER()在两边都能用。不过注意,MySQL 8.0 之前是不支持窗口函数的,老版本只能用@rownum这种用户变量模拟,性能一言难尽。如果你还在维护 MySQL 5.7,遇到“分组后取每组前 N 条”的需求,别硬写窗口函数,老老实实做关联子查询或者自增变量。

有个冷门问题,热搜里也有人问“SQL Server OFFSET 后再 TOP 20 查到的是什么”。这里要特别提醒:TOP和OFFSET混用非常危险。因为SELECT TOP(20) ... ORDER BY id OFFSET 10 ROWS的执行顺序里,OFFSET 是在 ORDER BY 之后先对结果集做跳过,TOP 是在查询最后取行,所以它的真实含义是“先排好序,跳过前 10 条,在剩余行里取前 20 条”。虽然逻辑上能自洽,但读代码的人很容易理解成“先取前 20,再跳过 10”,建议分页就老老实实用OFFSET FETCH,不要跟TOP混着写。

2.4 CASE WHEN 与分支逻辑

CASE WHEN表达式在 SQL Server 和 MySQL 里基本可以无缝切换,复杂条件聚合、行转列都靠它。比如统计每个用户满足不同条件的数量,两边写法几乎一样:

SELECT user_id, SUM(CASE WHEN status = 'A' THEN 1 ELSE 0 END) AS a_cnt, SUM(CASE WHEN status = 'B' THEN 1 ELSE 0 END) AS b_cnt FROM orders GROUP BY user_id;

唯一的坑是 NULL 判断:在CASE WHEN里判断 NULL,必须写WHEN col IS NULL,不能写WHEN col = NULL,这个规则两边通用,但新手很容易踩。另外,SQL Server 提供了IIF(condition, true_value, false_value),MySQL 对应的是IF(condition, true_value, false_value),函数名不一样,迁移的时候直接替换即可。NULLIF(a, b)两边通用,适合做“除数为零返回 NULL”的保护。

3. 进阶必备:表结构、事务、锁、多行合并

3.1 创建表:自增、默认值、字段类型差异

建表时最明显的差异是自增列。SQL Server 用IDENTITY(1,1),只允许在创建表或修改表时定义,插入时不能给自增列显式赋值,除非临时打开SET IDENTITY_INSERT ON。MySQL 用AUTO_INCREMENT,插入时如果想指定某个 ID,可以直接写,后续自增会接着你指定的最大值继续走。这个习惯差异看起来很细,但在做数据迁移、导入导出时影响挺大的。

重置自增值的语法也不一样。SQL Server 用:

DBCC CHECKIDENT ('表名', RESEED, 0);

MySQL 用:

ALTER TABLE 表名 AUTO_INCREMENT = 1;

字段类型映射方面,SQL Server 的NVARCHAR在 MySQL 里一般用VARCHAR就够;SQL Server 的DATETIME2对应 MySQL 的DATETIME;SQL Server 的UNIQUEIDENTIFIER对应 MySQL 的CHAR(36)或UUID()。特别注意TIMESTAMP:SQL Server 里的TIMESTAMP是行版本号,跟时间没关系;MySQL 里的TIMESTAMP是真时间,并且有可能自动更新。如果迁移时直接搬运建表语句,这会是一个隐蔽的坑。

默认值写法也不一样。SQL Server 当前时间默认值是DEFAULT GETDATE(),MySQL 是DEFAULT CURRENT_TIMESTAMP,并且TIMESTAMP类型还能配ON UPDATE CURRENT_TIMESTAMP自动改时间。修改表结构时,SQL Server 用ALTER TABLE t ALTER COLUMN c INT,MySQL 用ALTER TABLE t MODIFY COLUMN c INT,关键字都不一样,别直接套。

3.2 多行合并成一行:STUFF + FOR XML PATH 与 GROUP_CONCAT

多行合并是报表开发里的高频需求。SQL Server 7.0 时代的老写法到现在还经常见,用FOR XML PATH配合STUFF去掉开头分隔符:

SELECT STUFF(( SELECT ',' + name FROM sys.all_objects ORDER BY name FOR XML PATH('') ), 1, 1, '') AS names;

SQL Server 2017 开始有了官方函数STRING_AGG,写法简洁很多:

SELECT STRING_AGG(name, ',') FROM sys.all_objects;

MySQL 对应的是GROUP_CONCAT:

SELECT GROUP_CONCAT(name ORDER BY name SEPARATOR ',') FROM sys.all_objects;

这里有几个注意点。第一,FOR XML PATH会对 XML 特殊字符做转义,比如内容里有<、&会变成&lt;、&amp;,处理用户生成内容时要注意还原。第二,MySQL 的GROUP_CONCAT结果长度受group_concat_max_len限制,默认是 1024 字节,数据多了会静默截断,可以在会话级别SET SESSION group_concat_max_len = 102400;。第三,STRING_AGG和GROUP_CONCAT都支持DISTINCT,但语法位置略有不同,我已经记错过好几次,写的时候留意一下。

3.3 事务与隔离级别:默认值就能坑人

事务的基本写法两边很像:SQL Server 用BEGIN TRAN / COMMIT / ROLLBACK,MySQL 用START TRANSACTION / COMMIT / ROLLBACK,都能实现回滚。但默认隔离级别差异很大:SQL Server 默认是READ COMMITTED,MySQL 默认是REPEATABLE READ。这意味着同一个业务代码,在 SQL Server 里两次查询可能读到别的事务刚提交的数据,在 MySQL 里同一个事务内两次普通查询结果却是一致的(快照读)。如果迁移时没注意,很可能会出现“同一时间点统计结果对不上”的诡异问题。

事务里的错误处理差异更大。SQL Server 里习惯用BEGIN TRY / BEGIN CATCH / THROW包住事务,出错自动回滚;MySQL 里更常见的是先做校验再执行,或者在存储过程里用DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK。还有一点千万记住:MySQL 的 DDL 语句有隐式提交,一旦你执行了CREATE TABLE、ALTER TABLE等语句,当前事务就自动提交了,无法回滚;SQL Server 对 DDL 是支持事务性回滚的。所以那种“先建临时表、再更新正式表、出错全回滚”的脚本,在 MySQL 里很可能达不到预期效果。

定位事务问题时,SQL Server 可以查sys.dm_tran_locks结合sys.dm_exec_requests看阻塞,MySQL 8.0 用SHOW ENGINE INNODB STATUS看锁信息,或者查performance_schema.data_locks。这两个系统的排查思路完全不同,但第一步都是找到那个“卡住”的事务或会话,再往前追它持有的锁。

3.4 锁与查询提示:NOLOCK 别乱用

SQL Server 里很多人喜欢在查询里加WITH (NOLOCK),本质是让语句在READ UNCOMMITTED级别下读,好处是不容易被写事务阻塞,坏处是会读到未提交的脏数据。MySQL 里没有一句完全等价的查询提示,LOCK IN SHARE MODE和FOR UPDATE反而都是加锁读,跟 NOLOCK 方向完全相反。如果你在 SQL Server 里用惯了 NOLOCK,切到 MySQL 后想提高并发读性能,正确的做法是修改事务隔离级别:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

然后执行普通 SELECT。但最好想清楚这个操作是否真的需要脏读,报表、缓存、统计类场景可以接受,涉及资金、库存的就别赌了,老老实实让读写排队。

显式加锁方面,SQL Server 可以用WITH (UPDLOCK, HOLDLOCK)模拟“先查后改”过程中的范围锁,MySQL 里对应的是SELECT ... FOR UPDATE。两边都支持NOWAIT,但语义不完全一样,MySQL 的FOR UPDATE NOWAIT在 8.0 中遇到锁直接报错,SQL Server 的NOWAIT是用于指定等待时间。这些微妙的差异,多库并行开发时特别容易埋雷。

4. 存储过程、函数、系统表和迁移兼容

4.1 变量、临时表与批处理

写脚本时,SQL Server 里随手就是DECLARE @i INT = 0,然后SET @i = @i + 1,MySQL 日常命令行里用SET @i = 0直接声明用户变量。但 MySQL 的DECLARE关键字只能出现在存储过程、函数、触发器里,不能直接在 SQL 脚本里DECLARE @x。这个差异会导致你从 SQL Server 复制一段查询到 MySQL 客户端,第一行就报语法错误。

临时表也有差异。SQL Server 里会话级临时表用#temp,全局临时表用##temp,还有表变量@table,生命周期和作用域分得很细。MySQL 的临时表就是CREATE TEMPORARY TABLE,只对当前会话可见,断开连接自动删除。在连接池环境下要注意:MySQL 的临时表会跟着同一个物理连接走,如果连接复用,临时表可能残留到下一次请求里,这是很隐蔽的问题。SQL Server 的#temp虽然也会跟着会话,但连接池封装通常更严格,一般不会出现交叉污染。

批处理GO这个点值得再说一下。SQL Server 里变量作用域跟在批处理边界有关,DECLARE在第一个GO之前定义,GO之后就不能用了;MySQL 没有批处理边界,变量从定义到会话结束都有效。所以迁移长脚本时,如果原脚本里有很多GO,不能简单删掉完事,要检查变量作用域和事务边界是否被改变。

4.2 存储过程写法差异

存储过程是最能体现“两边都在写 SQL,但完全不是一个物种”的地方。SQL Server 创建存储过程:

CREATE PROCEDURE usp_GetUser @id INT = 0 AS BEGIN SELECT * FROM dbo.users WHERE id = @id; END; GO

调用时可以用命名参数:

EXEC usp_GetUser @id = 1;

MySQL 创建同样功能:

DELIMITER $$ CREATE PROCEDURE `GetUser`(IN `p_id` INT) BEGIN SELECT * FROM users WHERE id = p_id; END$$ DELIMITER ;

调用必须用CALL:

CALL GetUser(1);

除了语法,参数体系差异很大。SQL Server 的存储过程参数默认是输入参数,要输出得加OUTPUT;MySQL 的存储过程参数有IN、OUT、INOUT三种模式,OUT参数必须在调用时传入用户变量,比如先CALL sp_xxx(@result),再SELECT @result取值。另外,SQL Server 的存储过程里可以直接执行动态 SQLEXEC(@sql);MySQL 需要三段式:

SET @sql = 'SELECT * FROM users WHERE id = ?'; PREPARE stmt FROM @sql; EXECUTE stmt USING @id; DEALLOCATE PREPARE stmt;

动态 SQL 一定要做参数化处理,无论哪个库直接字符串拼接都容易出 SQL 注入,这个没得商量。

4.3 系统表与元数据查询:sys.objects vs information_schema

查表结构、查索引、查当前连接,两边差别也明显。SQL Server 有sys.tables、sys.columns、sys.indexes这一套系统视图,DBA 都喜欢直接查sys.;MySQL 更多用information_schema或者SHOW命令,比如SHOW TABLES和SHOW CREATE TABLE。information_schema两边都有,所以做通用工具时我优先用它。下面的对照表可以收藏:

需求SQL ServerMySQL
查看所有表SELECT name FROM sys.tables;SHOW TABLES;
查看表字段SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('t');SHOW COLUMNS FROM t;
查看建表语句sp_help 't';SHOW CREATE TABLE t;
查看当前版本SELECT @@VERSION;SELECT VERSION();
查看当前连接 IDSELECT @@SPID;SELECT CONNECTION_ID();

备份和恢复也是迁移必做的。SQL Server 的常规操作是BACKUP DATABASE生成.bak文件,再用RESTORE DATABASE恢复;MySQL 则是用mysqldump导出 SQL 文件,再用mysql < dump.sql导入。要注意.bak文件基本不能跨版本降级恢复,而 MySQL 的 SQL 导出文件只要注意字符集和存储引擎,兼容性相对好一点。碰到大数据量迁移,SQL Server 可以拆分多个备份文件,MySQL 则适合用--single-transaction保证一致性备份,细节不少。

5. 常见问题速查表与迁移踩坑记录

5.1 语法差异速查表

把上面所有内容浓缩成一张表,遇到拿不准的,先查这张表再动手:

功能SQL ServerMySQL
取前 N 行SELECT TOP (10) ...SELECT ... LIMIT 10
分页OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLYLIMIT 20, 10
字符串拼接a + b,注意 NULLCONCAT(a, b)
字符串转数字CAST('123' AS INT)CAST('123' AS SIGNED)
空值替代ISNULL(a, b)/COALESCE(a,b)IFNULL(a, b)/COALESCE(a,b)
多行合并STRING_AGG(name, ','),老版本用STUFF...FOR XML PATHGROUP_CONCAT(name)
当前时间GETDATE()NOW()
自增列IDENTITY(1,1)AUTO_INCREMENT
获取自增 IDSCOPE_IDENTITY()LAST_INSERT_ID()
条件表达式CASE WHEN、IIF()CASE WHEN、IF()
行号排名ROW_NUMBER() OVER(...)8.0 支持,5.7 用变量模拟
临时表#temp/@tableCREATE TEMPORARY TABLE
默认隔离级别READ COMMITTEDREPEATABLE READ
存储过程调用EXEC proc @p = 1CALL proc(1)
修改列类型ALTER TABLE t ALTER COLUMN c INTALTER TABLE t MODIFY COLUMN c INT
元数据查看sys.tables/sys.columnsSHOW TABLES/SHOW COLUMNS
日期格式化FORMAT(d, 'yyyy-MM-dd')DATE_FORMAT(d, '%Y-%m-%d')
多表 UPDATEUPDATE a SET ... FROM a JOIN b ON ...UPDATE a JOIN b ON ... SET ...
多表 DELETEDELETE a FROM a JOIN b ON ...DELETE a FROM a JOIN b ON ...(也可用)

这张表并不完整,但它覆盖了日常 CRUD、报表、事务开发里 80% 以上的高频需求。我自己的代码片段库里常年放着这份对照,每次换库先过一眼,能省很多试错时间。

5.2 迁移时最容易被忽略的 5 个坑

第一,字符集和排序规则。MySQL 5.7 默认utf8其实不是真正的 UTF-8,很多 emoji 和生僻字存不进去,8.0 默认utf8mb4好一些。SQL Server 默认排序规则类似SQL_Latin1_General_CP1_CI_AS,大小写不敏感,但同样的数据迁到 MySQL 的utf8mb4_bin或utf8mb4_unicode_ci后,排序规则变了,查询结果顺序和大小写匹配行为都可能变。

第二,零日期。MySQL 在严格模式下,0000-00-00这种日期会直接报错,而 SQL Server 的DATETIME默认就是1900-01-01,几乎没有零值概念。迁移旧数据时,先跑一遍SELECT ... WHERE date_col = '0000-00-00',把脏数据清洗干净再导。

第三,布尔类型。SQL Server 没有真正的 BOOLEAN,用BIT表示 0/1;MySQL 里有TINYINT(1)和BOOL关键词。虽然应用层拿到的结果都是 0/1,但如果你在 SQL 里写WHERE is_active = TRUE,两边解析方式会有差异,SQL Server 会把它当等于 1,MySQL 则看具体版本和类型,容易出幺蛾子。

第四,导入导出工具。SQL Server 的bcp和BACPAC适合大批量迁移,MySQL 的mysqldump更适合中小规模数据。我踩过的坑是直接用 mysqldump 导出的 SQL 里有UNLOCK TABLES和LOCK TABLES,在只读账号上跑会失败,需要用--skip-lock-tables。

第五,自增 ID 重置。把旧库数据导到新库后,如果没注意自增列当前值,插入新数据时可能主键冲突。SQL Server 用DBCC CHECKIDENT,MySQL 用ALTER TABLE ... AUTO_INCREMENT,两条命令都必须在数据导入完成后手动执行,别指望系统自动帮你续上。

最后分享一点我的实际操作习惯

这些年在两个库之间来回切换,我最开始也试图写一套“通用 SQL”来兼容两边,后来发现完全走不通。现在我的做法是:先明确当前在哪个数据库,再用对应的方言写,写完对照速查表过一遍。每次从 SQL Server 切换到 MySQL,我最先提醒自己的三件事是分页要改成 LIMIT、字符串拼接要改成 CONCAT、事务隔离级别默认值不一样;从 MySQL 切回 SQL Server,我就反过来检查 OFFSET FETCH、ISNULL、IDENTITY 这些点。这套方法不敢说能覆盖所有差异,但应对我日常 80% 的项目需求已经够了。建议你也找一个固定的切换顺序,每次都按这个顺序逐项检查,踩坑次数会明显降下来。

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

独立数据库 vs 集中式DAO:微服务数据架构选型与迁移实战

上周跟一个做后端架构的朋友吃饭&#xff0c;他正带着团队把一个“伪微服务”项目推翻重做。伪在哪里&#xff1f;所有业务代码确实拆成了十几个服务模块&#xff0c;但数据库还是一个MySQL实例&#xff0c;数据访问层是同一个DAO包&#xff0c;一张订单表十几个服务都在读写。…

作者头像 李华
网站建设 2026/10/6 13:25:20

雀魂牌谱结构化解析与数据分析实战指南

简介&#xff1a;MajsoulPaipuAnalyzer是一款面向雀魂&#xff08;Mahjong Soul&#xff09;玩家与数据分析爱好者的开源牌谱分析工具&#xff0c;支持国服、日服及国际服四人麻将牌谱解析&#xff0c;适用于希望深入复盘对局表现、对比天凤凤凰桌标准数据的中高级麻雀玩家。资…

作者头像 李华
网站建设 2026/10/6 13:24:40

Pylint 与 Flake8 如何配合?打造 Python 代码质量防线的实战指南

Pylint 和 Flake8 是我在 Python 项目里搭质量防线时&#xff0c;最先装上、也最常被讨论的两个工具。很多人觉得“代码能跑就行&#xff0c;lint 不过是找茬”&#xff0c;但当你被一个藏在角落的未使用变量坑过、被一段复杂度爆表的函数拖到不敢重构之后&#xff0c;就会明白…

作者头像 李华
网站建设 2026/10/6 13:24:37

Android广告SDK原理与核心源码解析:从请求链路到缓存策略

广告SDK这个东西&#xff0c;圈外人听着可能觉得就是个“往App里塞广告的库”&#xff0c;但真正在Android端做过商业化或聚合SDK的人都知道&#xff0c;它其实是一个非常精巧的客户端-服务端联动系统。早期很多开发者自己接广告&#xff0c;就是调一个SDK的load方法&#xff0…

作者头像 李华
网站建设 2026/10/6 13:24:26

Android WiFi关闭机制全解:从Framework到HAL的调用链与调试指南

Android的WiFi功能大家天天都在用&#xff0c;但真正遇到"点了关闭按钮&#xff0c;WiFi就是不关""关了半天都没反应""关闭后偶尔还会自动重连"这类问题时&#xff0c;如果只停留在应用层排查&#xff0c;往往一头雾水。我前阵子正好在跟一个WiF…

作者头像 李华
网站建设 2026/10/6 13:23:25

MySQL int(1) 与 int(10) 的区别:显示宽度背后的真相与版本演进

先问个问题&#xff1a;建表的时候看到int(10)&#xff0c;你的第一反应是什么&#xff1f;我猜不少人和我一样&#xff0c;一开始以为它和varchar(10)类似&#xff0c;表示这个字段最多只能存 10 个字符&#xff0c;所以int(1)就只能存一位数字。这个理解错得相当离谱&#xf…

作者头像 李华