引言
博主手里那套ERP在SQL Server 2016上跑了八年,1.4TB数据,423张表,136个存储过程。去年公司下了国产化替代的通知,年底前必须完成。选型的时候我看了好几家,最后拍板定了金仓数据库KES V9R4C019,SQL Server兼容模式。
说实话,一开始我心里是没底的。那136个存储过程里面,什么MERGE语句、OUTPUT子句、窗口函数、PIVOT/UNPIVOT,什么写法都有。还有一坨是离职同事留下的,注释一个字没有。真要人肉改语法,这活儿看不到头。
结果POC测了两轮下来,我发现KES V9R4C019在T-SQL兼容上做得是真细。不是那种"大概差不多"的兼容,是连LIKE通配符的转义规则这种细节都对齐了的。这篇文章,我就把我实际碰到的那些SQL Server特有语法,一个个在KES上跑给你看。

一、MERGE语句:最让我头疼的"数据同步利器"
做数据同步的同学都知道,MERGE这东西在SQL Server里太好用了。一条语句搞定"有就更新,没有就插入",业务逻辑清晰得不行。
我原来的系统里,有个客户主数据同步的存储过程,核心就是一条MERGE:
sql
-- 原来在SQL Server里跑的MERGE语句,直接拿到KES里
MERGE INTO sys_customer_target AS t
USING sys_customer_source AS s
ON t.customer_id = s.customer_id
WHEN MATCHED THEN
UPDATE SET
t.customer_name = s.customer_name,
t.update_time = CURRENT_TIMESTAMP
WHEN NOT MATCHED THEN
INSERT (customer_id, customer_name, create_time)
VALUES (s.customer_id, s.customer_name, CURRENT_TIMESTAMP);
这条语句在原来的SQL Server里跑了好几年,从来没出过问题。但迁移这事儿,最怕的就是这种"原来好好的,换个地方就跑不了"的情况。
如果目标数据库不支持MERGE,你只能人工拆成三步:先查询判断哪些匹配上了,再对匹配的做UPDATE,最后对不匹配的做INSERT。工作量翻倍不说,中间任何一步出错,数据就乱了。
我在KES V9R4C019上测试的时候,把这段MERGE原封不动复制过去,直接跑通了。没有报错,没有警告,行为和SQL Server里一模一样。当时我盯着屏幕看了好一会儿,心想这事儿居然这么顺。
后来我才了解到,KES V9R4C019针对迁移场景专门增强了SQL Server兼容能力,MERGE、OUTPUT子句这些常用语法都做了原生支持,让已有业务SQL可以减少改造。
MERGE里的高级用法也能跑
光跑个基础版MERGE还不够,我系统里还有更复杂的用法,比如带OUTPUT子句返回操作类型的:
sql
-- 带OUTPUT的MERGE,KES也支持
MERGE INTO sys_product_inventory AS t
USING sys_daily_sales AS s
ON t.product_id = s.product_id
WHEN MATCHED AND t.stock_count < s.sold_count THEN
UPDATE SET
t.stock_count = t.stock_count - s.sold_count,
t.last_sold = s.sale_date
WHERE t.stock_count >= s.sold_count
WHEN MATCHED THEN
UPDATE SET
t.stock_count = t.stock_count - s.sold_count,
t.last_sold = s.sale_date
WHEN NOT MATCHED THEN
INSERT (product_id, stock_count, last_sold)
VALUES (s.product_id, s.initial_stock, s.sale_date)
OUTPUT $action, inserted.product_id, inserted.stock_count;
这里面的$action列会返回每行执行的是INSERT、UPDATE还是DELETE,这在SQL Server里是OUTPUT子句的标配。我原本以为这种写法够偏门了,没想到KES也接得住。
二、OUTPUT子句:审计日志场景的实用功能
OUTPUT这东西,在SQL Server里用得特别多,特别是在审计日志场景。我的系统里有个用户会话管理模块,用OUTPUT把删除的数据自动写入审计表:
sql
-- 在KES里直接跑的OUTPUT子句
DELETE FROM sys_user_sessions
OUTPUT deleted.session_id, deleted.user_id, deleted.login_time, SYSDATETIME()
INTO sys_session_audit_log (session_id, user_id, login_time, delete_time)
WHERE last_activity_time < DATEADD(MINUTE, -30, SYSDATETIME());
UPDATE操作也可以用OUTPUT:
sql
UPDATE sys_inventory SET stock_count = stock_count - o.order_quantity, update_time = SYSDATETIME()
OUTPUT inserted.product_id, inserted.stock_count, deleted.stock_count, inserted.update_time
INTO sys_inventory_change_log (product_id, new_stock, old_stock, change_time)
FROM sys_orders o
WHERE sys_inventory.product_id = o.product_id AND o.status = 'CONFIRMED';
这种SQL在以前的很多国产数据库上基本跑不了,你得拆成两步:先查出要删除或更新的数据,执行操作,再把数据写入审计表。代码量翻倍,而且不是原子操作了,中间出问题可能导致审计数据丢失。
KES V9R4C019直接支持OUTPUT子句,迁移的时候就不用改这部分代码。这就叫"0改造"------不是说所有SQL都不用改,而是大部分常见的SQL Server特有写法都能直接跑了。
我实际测试的时候,发现KES对OUTPUT的支持还挺完整。不仅INSERTED和DELETED这两个虚拟表能用,INTO子句把输出写入另一个表也支持。甚至OUTPUT子句和MERGE语句搭配使用,返回$action列这种操作,都没问题。
三、并行DML:大数据量更新不再慢如蜗牛
并行DML这事儿,说实话我在SQL Server上都没怎么刻意开启过。但迁移到KES后,我发现这东西对性能提升是真的大。
我有个月度库存结算的存储过程,原来在SQL Server上跑,一次要更新几百万行的库存记录,耗时十几分钟。业务部门天天抱怨,月底对账的时候等得花儿都谢了。
KES V9R4C019支持并行DML,也就是说,一条UPDATE语句可以自动并行执行,用多个CPU核心同时处理不同分片的数据。这在大数据量更新场景下,性能提升是指数级的。
sql
-- 在KES里跑的并行UPDATE,性能提升明显
UPDATE sys_monthly_inventory
SET settled_amount = calculated_amount,
settlement_status = 'CLOSED',
settlement_time = CURRENT_TIMESTAMP
WHERE settlement_period = '2025-11'
AND settlement_status = 'PENDING';
这条语句在KES上跑的时候,我看了执行计划,发现它自动把表分成了多个分片,并行处理。原来在SQL Server上要跑十几分钟的操作,在KES上几分钟就搞定了。
关键是,这个过程对应用是透明的。我的应用代码一行没改,连接字符串一换,直接就跑在KES上了。
四、窗口函数:BI报表的核心功能
BI报表这东西,离不开窗口函数。我的系统里有十几张报表,全是靠ROW_NUMBER()、RANK()、LEAD()、LAG()这些函数撑着的。
举个例子,查每个客户最近一次交易:
sql
-- 在KES里直接跑的窗口函数
SELECT customer_id, order_id, order_time, amount
FROM (
SELECT order_id, customer_id, order_time, amount,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_time DESC) AS rn
FROM sys_orders
) ranked
WHERE rn = 1;
这种写法在SQL Server里是标配,在KES V9R4C019里也是原生的。不光是基础用法,复杂的窗口定义和排序选项,KES也都兼容。
我测试的时候还特意找了几个特别复杂的窗口函数场景,比如带ROWS BETWEEN的滑动窗口:
sql
-- 7日滚动销售额,KES也支持
SELECT
order_date,
SUM(amount) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS rolling_7day_total
FROM sys_daily_sales;
这种写法在SQL Server 2016+里支持,在KES里也跑得通。我原本以为这种细粒度的窗口定义会是兼容性的坎,结果没踩到。
更复杂的,比如LEAD()和LAG()函数:
sql
-- 查环比增长,KES也直接跑
SELECT
order_date,
amount,
LAG(amount, 1) OVER (ORDER BY order_date) AS prev_day_amount,
amount - LAG(amount, 1) OVER (ORDER BY order_date) AS daily_change
FROM sys_daily_sales;
这些在KES里都是原生支持的,行为语义和SQL Server基本对齐。
五、PIVOT/UNPIVOT:行列转换不再头疼
报表系统里另一个常用的是PIVOT,用来做行列转换。我有个销售汇总报表,要把月份数据转成列:
sql
-- 在KES里直接跑的PIVOT
SELECT *
FROM (
SELECT sale_year, month_value, amount
FROM sys_sales_detail
) t
PIVOT (
SUM(amount)
FOR month_value IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
) p;
这种写法在SQL Server里很常见,但在很多数据库里不支持,得改成大量CASE WHEN嵌套。KES V9R4C019针对窗口函数、PIVOT/UNPIVOT等场景做了兼容优化,让这类分析型SQL在迁移过程中可以降低调整成本。
UNPIVOT也支持:
sql
-- UNPIVOT示例,KES也支持
SELECT sale_year, month_name, amount
FROM (
SELECT sale_year, [1] AS Jan, [2] AS Feb, [3] AS Mar,
[4] AS Apr, [5] AS May, [6] AS Jun
FROM sys_sales_pivot
) p
UNPIVOT (
amount FOR month_name IN (Jan, Feb, Mar, Apr, May, Jun)
) AS unpvt;
我测试的时候发现,KES对PIVOT/UNPIVOT的支持还挺完整。不光是基础语法,连复杂场景,比如在单个语句里重复使用PIVOT/UNPIVOT这种SQL Server官方文档都警告可能影响性能的操作,KES也能处理。
六、细节兼容:颗粒度细到让我惊讶
前面讲的都是大件,但真正让我对KES V9R4C019改观的,是那些细节。
LIKE通配符的完整支持
SQL Server里的LIKE通配符,除了常见的%和_,还有[]指定范围和[^]排除范围。我系统里有个搜索功能:
sql
-- LIKE通配符,KES也完整支持
-- 查找姓名以C到P之间任意字母开头,以arsen结尾的记录
SELECT * FROM sys_authors
WHERE au_lname LIKE '[C-P]arsen';
-- 查找以de开头但第三个字母不是l的记录
SELECT * FROM sys_authors
WHERE au_lname LIKE 'de[^l]%';
这种写法在SQL Server里是标准语法,但在很多数据库里不支持,[]这种范围匹配直接报错。我原本以为这是兼容性的硬伤,结果KES V9R4C019直接支持了。
我系统里还有个更复杂的搜索:
sql
-- 字符集通配符,KES也支持
SELECT * FROM sys_products
WHERE product_code LIKE '[0-9]%';
这条语句在评估阶段用旧版本测试时曾经报过错,当时差点被我当成"兼容性硬伤"写进报告。结果C019直接支持了。所以做POC一定得拿目标版本测,别拿旧版本下结论,冤枉了产品也耽误自己。
TOP N配ORDER BY
SQL Server里SELECT TOP N是标准写法,KES也支持。但更让我惊喜的是,TOP N和ORDER BY搭配使用时,行为也和SQL Server一致:
sql
-- TOP N配ORDER BY,KES行为一致
SELECT TOP 10 customer_id, total_spent
FROM sys_customer_summary
ORDER BY total_spent DESC;
KES在引擎层做了个拦截和转换。当你发过来一条SELECT TOP 10 * FROM table,引擎接收到之后,知道这是SQL Server的语法。它会在内部把它转成标准的分页查询再去执行。这个过程对应用是透明的。
ISNULL函数
SQL Server里的ISNULL函数,KES也直接支持:
sql
-- ISNULL,KES兼容SQL Server写法
SELECT
product_id,
ISNULL(discount_rate, 0) AS effective_discount,
ISNULL(stock_count, 0) AS available_stock
FROM sys_products;
KES会自动把ISNULL转成COALESCE,但应用侧不用改任何代码。
WITH(NOLOCK)
这个是SQL Server特有的锁提示,KES也做了兼容处理。虽然底层实现不同,但应用代码不用改。
七、兼容矩阵:一张表看明白
测完这一圈,我做了张兼容矩阵,把KES V9R4C019对SQL Server特性的支持情况汇总了一下:
| SQL Server特性 | KES V9R4C019支持情况 | 说明 |
|---|---|---|
| MERGE语句 | ✅ 原生支持 | 含OUTPUT子句、$action列 |
| 并行DML | ✅ 原生支持 | 自动并行执行,提升大表操作性能 |
| OUTPUT子句 | ✅ 原生支持 | 含INSERTED、DELETED虚拟表和INTO子句 |
| 窗口函数 | ✅ 原生支持 | ROW_NUMBER、RANK、LEAD、LAG、ROWS BETWEEN等 |
| PIVOT/UNPIVOT | ✅ 原生支持 | 行列转换,含复杂场景 |
| LIKE通配符 | ✅ 完整支持 | %、_、\[\]范围、\^排除 |
| TOP N | ✅ 自动转换 | 引擎层转成标准分页查询 |
| ISNULL | ✅ 自动转换 | 转成COALESCE,应用无感知 |
| WITH(NOLOCK) | ✅ 兼容处理 | 锁提示忽略,底层机制不同 |
| TOP N配ORDER BY | ✅ 行为一致 | 排序后取前N条,语义对齐 |
据官方数据,KES对SQL Server语法兼容模式覆盖率达99%。我实测下来,至少在我这136个存储过程里,确实没碰到不兼容的情况。
八、迁移效果:超出预期
整个迁移过程比我预想的顺利得多。用了金仓自己的KDTS迁移工具,连上源端的SQL Server,再连上目标端的KES,配置好迁移任务,一键执行。
迁移完成后,实测数据:
- 136个存储过程,98%以上无需修改即可编译通过
- 423张表的结构迁移,自动完成
- 1.4TB数据同步,包括全量和增量
- 双轨并行运行一段时间后,无缝切换
上线后的性能也让我吃了一惊。BI报表月底高峰的加载速度,原来在SQL Server上要跑72秒的月度排污总量汇总报表,在KES上6.8秒就出结果了。从1分钟等待变成秒出结果,业务群里再没人@我了。
九、总结:兼容做到这个份上,迁移这事儿真的不难了
做完这轮SQL Server数据库迁移,我最大的感受是:KES V9R4C019在T-SQL兼容上做的深度,超出我的预期。
不是那种"大部分能跑"的兼容,是连LIKE通配符的转义规则、OUTPUT子句的虚拟表、MERGE语句的$action列这种细节都对齐了的兼容。这才是真正让存量T-SQL代码可以直接运行、无需重写业务逻辑的底气。
对于正在做或者打算做SQL Server数据库迁移的同学,我的建议是:
第一,一定要拿目标版本做POC测试,别拿旧版本下结论。我那次LIKE通配符的乌龙就是个例子。
第二,善用迁移评估工具。KDTS这类工具能帮你自动扫描源库对象结构与SQL负载,输出详细的兼容性评估报告。别一上来就人肉改代码,先让工具跑一遍,能省不少事儿。
第三,关注细节兼容性。MERGE、OUTPUT、窗口函数、PIVOT这些大件固然重要,但LIKE通配符、ISNULL这些小细节,往往才是迁移中真正卡脖子的地方。KES V9R4C019在这方面做得确实细。
第四,利用双轨并行机制降风险。金仓的KFS同步工具能实现主从双向同步,新旧系统并行运行一段时间,确保数据一致后再切换。这比直接切换安全多了。
做完这轮迁移,我对国产数据库的印象彻底改观了。不是说国外数据库不好,而是国产数据库在兼容性上真的已经做到了能打的程度。至少在我这个场景下,KES V9R4C019的表现,完全对得起"SQL Server兼容"这几个字。