SQL Server 迁到金仓,真正麻烦的不是数据,而是这些 T-SQL
去年参与过一个比较典型的数据库迁移项目。
客户原来的系统比较杂,SQL Server、Oracle、MySQL 都有,最后需要统一迁到金仓。涉及的系统有 11 个,其中不少已经运行很多年,业务代码和数据库绑定得比较深。
项目刚开始的时候,我其实不太担心数据。
表数据迁移现在已经有比较成熟的工具了,真正让我没底的是另外一件事:
SQL Server 里那些用了很多年的 T-SQL,迁过去以后到底要改多少?
这个问题直接决定项目工作量。 
以前做数据库迁移,最耗时间的往往不是建表、导数据,而是处理存储过程、函数、触发器和业务 SQL。尤其 SQL Server 项目,经常能看到 MERGE、OUTPUT、TOP、IDENTITY、PIVOT,再加上一堆 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 的语法?
现在做过几次实际迁移以后,我觉得这个问题至少应该拆成四层。
第一层是语法能不能执行。
比如 MERGE、OUTPUT、TOP、窗口函数、PIVOT。
第二层是行为是不是一致。
SQL 能执行,但 NULL 处理、隐式类型转换、排序规则、日期处理结果不一样,同样可能出事故。
第三层是性能能不能接受。
同一条 SQL,在不同优化器下面可能出现完全不同的执行计划。所以即使代码一行没改,关键 SQL 还是要重新压测。
第四层才是我现在最关注的:
到底要改多少存量代码。
因为对于一个运行了五年、八年甚至十年的系统来说,迁移最大的成本往往不是数据库授权,也不是数据传输。
而是人。
你需要多少开发去改代码,需要多少测试重新做回归,需要多少业务人员重新核对数据,这些最终都会变成项目成本。
最后说点项目上的感受
这次 SQL Server 迁移给我最大的感受,并不是"某个数据库兼容性有多高"。
而是让我重新认识了迁移项目应该怎么做。
以前容易把数据库迁移理解成:
把 A 数据库换成 B 数据库。
实际做下来,更准确的说法应该是:
尽可能在不改变原有业务行为的前提下,把系统底层数据库替换掉。
这两个定义差别很大。
前一个关注的是数据有没有搬过去。
后一个关注的是业务代码改了多少、SQL 行为有没有变化、性能有没有退化、数据是否一致,以及最终割接能不能控制在业务允许的时间窗口内。
所以,如果现在再让我接一个 SQL Server 到金仓的项目,我不会一上来就讨论"多久能迁完"。
我会先把 SQL 和数据库对象扫描一遍。
看看有多少 MERGE,多少 OUTPUT,多少存储过程,多少 SQL Server 特有函数,有没有复杂报表,有没有大对象,有没有同名对象,再把核心 SQL 的执行计划和数据量摸清楚。
这些东西搞清楚了,迁移周期基本也就有数了。
至于 KES V9R4C019 这次给我的实际感受,可以概括成一句比较朴素的话:
迁 SQL Server 最省事的地方,不是它能帮你搬多少数据,而是原来的代码能少改多少。
对一个真正干过迁移项目的人来说,这往往才是最值钱的地方。