SQL Server 迁到金仓,真正麻烦的不是数据,而是这些 T-SQL

SQL Server 迁到金仓,真正麻烦的不是数据,而是这些 T-SQL

去年参与过一个比较典型的数据库迁移项目。

客户原来的系统比较杂,SQL Server、Oracle、MySQL 都有,最后需要统一迁到金仓。涉及的系统有 11 个,其中不少已经运行很多年,业务代码和数据库绑定得比较深。

项目刚开始的时候,我其实不太担心数据。

表数据迁移现在已经有比较成熟的工具了,真正让我没底的是另外一件事:

SQL Server 里那些用了很多年的 T-SQL,迁过去以后到底要改多少?

这个问题直接决定项目工作量。

以前做数据库迁移,最耗时间的往往不是建表、导数据,而是处理存储过程、函数、触发器和业务 SQL。尤其 SQL Server 项目,经常能看到 MERGEOUTPUTTOPIDENTITYPIVOT,再加上一堆 GETDATE()ISNULL()CONVERT()

单独看每一种语法都不复杂,但一个运行了七八年的系统里,这些东西可能散落在几百个存储过程和大量业务代码中。

如果目标数据库不兼容,麻烦就来了。

所以这次项目一开始,我们没有急着搬数据,而是先把数据库对象和 SQL 扫了一遍。最后实际迁移下来,比最开始预估的改造量小很多。

用的版本是金仓 KES V9R4C019。

下面不讲太多产品参数,就挑几个 SQL Server 迁移时比较容易踩坑的地方说。

先说 MERGE,这东西一旦不兼容,改起来挺烦

SQL Server 项目里 MERGE 很常见。

特别是数据同步、状态更新、批处理这类场景,一个 SQL 就能把 INSERT、UPDATE,甚至 DELETE 一起处理。

例如:

sql 复制代码
MERGE target_table AS target
USING source_table AS source
ON target.id = source.id
WHEN MATCHED AND source.status = 'deleted' THEN
    DELETE
WHEN MATCHED AND source.status = 'updated' THEN
    UPDATE SET target.name = source.name
WHEN NOT MATCHED THEN
    INSERT (id, name) VALUES (source.id, source.name);

这种 SQL 看起来没什么,但迁移的时候最怕目标数据库只支持一部分语法。

如果不能直接执行,就得把一个 MERGE 拆成 UPDATE、INSERT、DELETE,再重新处理判断条件和事务。

SQL 一拆,测试量也跟着上去了。

这次使用 KES V9R4C019 时,这类 MERGE 基本没有成为迁移的主要障碍,原来的业务逻辑大部分可以继续保留。

这件事对新项目可能没什么感觉,但对老系统迁移很重要。

因为数据库迁移最怕的就是:

本来只想换个数据库,最后把业务代码也重构了一遍。

一旦走到这一步,项目性质就完全变了。


OUTPUT 也是个很容易被低估的东西

还有一个 SQL Server 项目经常碰到的语法是 OUTPUT

比如:

sql 复制代码
INSERT INTO orders (customer_id, amount, order_date)
OUTPUT inserted.order_id, inserted.customer_id, inserted.amount
VALUES (1001, 500.00, '2026-01-15'),
       (1002, 300.00, '2026-01-16');

很多人第一次做迁移的时候会觉得:

"不就是返回一下刚插入的数据吗?"

但真正翻业务代码以后会发现,事情没这么简单。

有些系统会拿 OUTPUT 返回的主键继续做后续处理,有些拿它记录审计日志,还有一些批量任务直接依赖它获取 DML 操作结果。

如果目标数据库不支持原来的行为,你不仅要改 SQL,还得顺着调用链检查 Java 代码。

这才是麻烦的地方。

这次迁移中,OUTPUT 相关 SQL 没有大面积重写,原来的 INSERT、UPDATE、DELETE 处理逻辑基本可以延续下来。

对迁移项目来说,这种"代码不用动"往往比多几个新功能更有价值。


BI 系统真正让我担心的是窗口函数和 PIVOT

客户系统里还有不少统计和报表 SQL。

这类 SQL 往往比普通 CRUD 难迁。

比如:

sql 复制代码
ROW_NUMBER()
RANK()
LAG()
LEAD()

再加上:

sql 复制代码
PIVOT
UNPIVOT

业务系统里的普通查询,即使不兼容,改一改通常也就几十行。但报表 SQL 不一样,一条 SQL 两三百行并不少见,各种子查询、窗口函数、CASE WHEN 和聚合套在一起。

这种 SQL 我一般不太愿意"为了迁移而重写"。

因为你很难保证改完以后每一个统计口径都和以前完全一样。

这次比较省心的一点,就是原有窗口函数以及 PIVOT / UNPIVOT 相关 SQL 大部分能够继续使用。

当然,兼容不代表性能一定完全一样。

这一点我觉得反而更值得强调。

SQL 能执行,只能证明第一关过了。真正上线之前还是得重新看执行计划、索引和统计信息。

我们碰到过一条环保监测汇总 SQL,迁移后功能没有问题,但还是针对执行计划重新做了索引调整。优化之后,原来十几秒的查询降到了两秒左右。

所以数据库迁移不能只做:

text 复制代码
SQL执行成功 = 迁移完成

更合理的检查应该是:

text 复制代码
语法兼容
   ↓
结果一致
   ↓
执行计划检查
   ↓
性能压测
   ↓
上线

这几个步骤少一个,后面都有可能补课。


真正磨人的,反而经常是 TOP、IDENTITY 这种"小东西"

大型语法大家都会提前关注。

真正迁移的时候,经常卡人的反而是一些不起眼的细节。

比如 SQL Server 开发人员随手就写:

sql 复制代码
SELECT TOP 10 *
FROM orders
ORDER BY create_time DESC;

或者建表的时候:

sql 复制代码
id INT IDENTITY(1,1)

代码里面又到处都是:

sql 复制代码
GETDATE()
ISNULL()
CONVERT()

单看一个都不难改。

问题是数量。

一个函数改 5 分钟没什么,如果项目里出现 3000 次,就完全是另一回事。

所以我现在做数据库迁移评估,已经不太喜欢只问一句:

"兼不兼容 SQL Server?"

这个问题太大了。

我更愿意直接扫描现有系统,看它到底用了哪些 SQL Server 特性,每一种出现多少次,哪些可以直接运行,哪些需要转换。

这才和实际工作量有关。


有个问题特别有意思:sys_user 重名

这次项目里还碰到了一个挺典型的小问题。

业务系统本身有一张:

text 复制代码
sys_user

而数据库侧也存在同名系统对象。

刚发现的时候,第一反应是:

"不会还得改表名吧?"

如果真要改,就比较麻烦了。

因为一个用了很多年的基础用户表,可能被 Java 代码、MyBatis XML、存储过程、视图甚至第三方程序同时引用。

改一张表名,后面可能跟着一串东西。

后来排查发现没有必要动业务表,通过 JDBC 连接参数调整就把这个问题处理掉了。

这件事虽然很小,但其实挺能说明数据库迁移里的一个原则:

能通过兼容配置解决的问题,尽量不要直接改业务代码。

尤其是老系统。

代码改得越少,回归测试范围越小,迁移风险通常也越容易控制。


我现在做迁移,第一步已经不是"导数据"了

以前接到数据库迁移任务,很多人的第一反应是:

建库、建表、导数据。

现在我基本不会这么干。

我更习惯先评估。

这次也是先通过 KDMS 对数据库对象做扫描,把表、视图、存储过程等对象先梳理出来,再看哪些能够直接迁移,哪些需要人工处理。

这个步骤最大的价值并不是帮你"自动改代码"。

而是让项目开始之前就知道:

到底有多少代码需要改。

这很关键。

假设有 200 个存储过程。

一种情况是 190 个基本不用动,10 个需要调整;

另一种情况是 100 个都要人工重写。

虽然都是"200 个存储过程",但两个项目的工期完全不是一个级别。

所以迁移评估报告真正应该解决的是项目经理最关心的问题:

这次迁移到底需要多少人、多少时间、风险在哪里?

而不是简单告诉你一句"支持 SQL Server 迁移"。


数据迁过去以后,我反而更关心"对不对"

SQL 改完,数据同步完成,也不能马上切。

因为迁移项目最后真正让人睡不着的问题通常只有一个:

两边的数据到底是不是一样?

数据量小的时候,还能人工查:

sql 复制代码
SELECT COUNT(*) FROM table_a;

但数据量一大,这种方式基本只能确认"条数差不多"。

条数一样,不代表内容一样。

所以实际迁移过程中,全量迁移、增量同步和最终校验最好拆开看。

我们这类项目一般会按照类似下面的过程推进:

text 复制代码
迁移评估
   ↓
对象转换
   ↓
全量数据迁移
   ↓
增量同步
   ↓
数据校验
   ↓
停止源端写入
   ↓
追平增量
   ↓
最终校验
   ↓
业务切换

KDTS、KFS 这类工具的价值也主要体现在这里。

尤其正式割接的时候,真正宝贵的是那几个小时甚至几十分钟的窗口。

如果前面已经把全量数据搬完,切换当天只需要追最后一段增量,再做校验,压力会小很多。


这次迁移之后,我对"数据库兼容"的理解变了

以前看到数据库厂商说"兼容 SQL Server",我第一反应往往是:

能不能执行 SQL Server 的语法?

现在做过几次实际迁移以后,我觉得这个问题至少应该拆成四层。

第一层是语法能不能执行

比如 MERGEOUTPUTTOP、窗口函数、PIVOT

第二层是行为是不是一致

SQL 能执行,但 NULL 处理、隐式类型转换、排序规则、日期处理结果不一样,同样可能出事故。

第三层是性能能不能接受

同一条 SQL,在不同优化器下面可能出现完全不同的执行计划。所以即使代码一行没改,关键 SQL 还是要重新压测。

第四层才是我现在最关注的:

到底要改多少存量代码。

因为对于一个运行了五年、八年甚至十年的系统来说,迁移最大的成本往往不是数据库授权,也不是数据传输。

而是人。

你需要多少开发去改代码,需要多少测试重新做回归,需要多少业务人员重新核对数据,这些最终都会变成项目成本。


最后说点项目上的感受

这次 SQL Server 迁移给我最大的感受,并不是"某个数据库兼容性有多高"。

而是让我重新认识了迁移项目应该怎么做。

以前容易把数据库迁移理解成:

把 A 数据库换成 B 数据库。

实际做下来,更准确的说法应该是:

尽可能在不改变原有业务行为的前提下,把系统底层数据库替换掉。

这两个定义差别很大。

前一个关注的是数据有没有搬过去。

后一个关注的是业务代码改了多少、SQL 行为有没有变化、性能有没有退化、数据是否一致,以及最终割接能不能控制在业务允许的时间窗口内。

所以,如果现在再让我接一个 SQL Server 到金仓的项目,我不会一上来就讨论"多久能迁完"。

我会先把 SQL 和数据库对象扫描一遍。

看看有多少 MERGE,多少 OUTPUT,多少存储过程,多少 SQL Server 特有函数,有没有复杂报表,有没有大对象,有没有同名对象,再把核心 SQL 的执行计划和数据量摸清楚。

这些东西搞清楚了,迁移周期基本也就有数了。

至于 KES V9R4C019 这次给我的实际感受,可以概括成一句比较朴素的话:

迁 SQL Server 最省事的地方,不是它能帮你搬多少数据,而是原来的代码能少改多少。

对一个真正干过迁移项目的人来说,这往往才是最值钱的地方。

相关推荐
Kyrie_kk1 小时前
Java--IO--Path文件访问
java·后端
wxwx_bscxy3222 小时前
基于springboot宠物领养系统的设计与实现
数据库·spring boot·后端·spring·宠物
分支预测失败2 小时前
RISC-V AIA 中断架构实战:从 PLIC 到 APLIC 与 IMSIC 的迁移
linux·后端
源代码•宸2 小时前
前置准备:定时微服务背景和现状
开发语言·经验分享·后端·微服务·云原生·架构·golang
我的div丢了肿么办2 小时前
go语言中的map,map定义不同的数据类型,循环map,map的无序性
后端·go
oliver_sys_log3 小时前
RocketMQ 4.5.1 延迟消息"发送成功但消费不到"排查分析报告
后端·rocketmq
码上观网3 小时前
VXLAN 中的 ARP 代答:从原理到 SONiC 代码实现
后端
计算机毕设定制辅导-无忧学长4 小时前
《基于SpringBoot的马术俱乐部管理系统》
java·spring boot·后端
摇滚侠4 小时前
《SpringBoot 3:入门与应用实战》第 12 章 JDBC 与事务 JDBC 事务管理 阅读笔记 33
spring boot·笔记·后端