ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SQL Server 迁电科金仓:那三周我记下的真事

SQL Server 迁电科金仓:那三周我记下的真事

先说背景,不是 demo。

源端 SQL Server 2017,业务库 650G,220 多张表,存储过程上百个,触发器、游标、表变量满地都是。跑的是制造业 ERP,白天高并发,夜里批处理。割接窗口只给凌晨两点到四点。

目标库是电科金仓 KingbaseES,后面就叫 KES。

我带两个后端、一个 DBA 老张,干了三周。下面按时间线写,想到哪说到哪,不按教科书来。

第一晚就翻车

我一开始特自信,觉得 KDTS 一拖完事。老张在旁边说:“你兼容模式开了没?”

我愣了。没开。

ALTER SYSTEM SET compatible_sqlserver = 'on'; ALTER SYSTEM SET sqlserver_compatible_version = '150'; SELECT sys_restart_db();

重启完再跑 KDMS 评估,原来红了一屏的“不兼容”直接消掉大半。TOP、[]、GETDATE()、#temp全认了。老张端着咖啡说:“没穿鞋就跑步。”

从那以后我们组死规矩:迁移评审第一项,查兼容模式。没开,评估报告作废。

顺带说,KES 的系统视图也做了映射,sys.objects、sys.columns那些,应用里靠元数据扫表结构的代码不用重写,这点挺省心。

建表:强迫症发作

原库订单表:

CREATE TABLE Sales.Orders ( OrderID INT IDENTITY(1,1) PRIMARY KEY, OrderNo NVARCHAR(32) NOT NULL, Amount DECIMAL(18,2) NOT NULL, Status TINYINT NOT NULL DEFAULT 0, OrderDate DATETIME DEFAULT GETDATE() );

兼容模式开着,粘过去能跑。但我看到NVARCHAR就手痒,非得改成VARCHAR。KES 默认 UTF-8,中文不需要 N 前缀,留着只会让后来的人困惑。

最后落地的版本:

CREATE TABLE Sales.Orders ( OrderID INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, OrderNo VARCHAR(32) NOT NULL, Amount NUMERIC(18,2) NOT NULL, Status SMALLINT NOT NULL DEFAULT 0, OrderDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

TINYINT升到SMALLINT,以前有个项目用 byte 映射状态字段,一算溢出,查了半天才定位到。DATETIME换TIMESTAMP,跟应用层 DateTime 对接时毫秒精度不打架。

类型映射我们记了张表,后面新表全照这个来:

SQL Server

KES

NVARCHAR(n)

VARCHAR(n)

DATETIME

TIMESTAMP

DECIMAL(18,2)

NUMERIC(18,2)

UNIQUEIDENTIFIER

UUID 兼容类型

BIT

BOOLEAN

rowversion 那玩意儿我们一开始建在 test 库报错,后来切到业务库才正常。这种坑文档不会写,得自己撞。

插数据:原脚本一个字没动

INSERT INTO Sales.Orders (CustomerID, OrderNo, Amount, Status) VALUES (1001, 'SO-2603-001', 12800.00, 1), (1001, 'SO-2603-002', 4600.00, 1), (1002, 'SO-2603-003', 23500.00, 2);

要手填自增主键就开开关:

SET IDENTITY_INSERT Sales.Orders ON; INSERT INTO Sales.Orders (OrderID, CustomerID, OrderNo, Amount, Status) VALUES (9999, 1005, 'SO-MANUAL-001', 100.00, 1); SET IDENTITY_INSERT Sales.Orders OFF;

注意这个开关是会话级的,跟 SQL Server 里行为有点差异。我们批处理脚本里特意显式关回来,不然后面会话串了容易出怪问题。

查询:TOP 居然直接过

SELECT TOP 10 [OrderID], [OrderNo], ISNULL(NULLIF(Status, 0), 9) AS StatusFlag FROM Sales.Orders WHERE OrderDate >= '2026-03-01' ORDER BY Amount DESC;

跑出来的时候我还有点不敢信。方括号、ISNULL、NULLIF 全认。

新写的接口我尽量用标准写法LIMIT 10,老代码先冻着。别一口气全改,改多了容易出事,前端那哥们会骂人的。

联表更新,最怕翻车的地方直接过了

SQL Server 里那种UPDATE ... FROM JOIN的写法:

UPDATE o SET o.Status = 2 FROM Sales.Orders o JOIN Sales.Customers c ON o.CustomerID = c.CustomerID WHERE c.City = '杭州' AND o.Status = 1;

KES 直接吃。我拿生产里一个两百多行的存储过程试水,里面七八个联表更新全过。那一刻后背汗下来了,真的。

删除要留痕:OUTPUT 救了审计

DELETE FROM Sales.Orders OUTPUT DELETED.OrderNo, DELETED.Amount WHERE Status = 9 AND OrderDate < '2025-01-01';

更新时拿前后值:

UPDATE Sales.Orders SET Amount = Amount * 0.9 OUTPUT INSERTED.OrderNo, DELETED.Amount AS OldAmount, INSERTED.Amount AS NewAmount WHERE Status = 1 AND Amount > 20000;

老王本来都写好重写方案了,看到这结果把文档删了,说白熬两晚。

分页:同秒数据坑过我

OFFSET FETCH没问题:

SELECT OrderID, OrderNo FROM Sales.Orders ORDER BY OrderDate DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY;

但深层分页一定要带主键。有次前端翻页第三页开始重复,查了两小时发现是OrderDate有同秒数据,排序不稳定。后来我直接定规矩:分页必须ORDER BY OrderDate DESC, OrderID DESC。前端那哥们翻白眼,但数据确实再没重复过。

日期函数别用DATEADD硬写,换成:

SELECT CURRENT_TIMESTAMP - INTERVAL '7' DAY;

临时表更不用管,#tmp、##global随便用,报表那堆存储过程基本零改动。

MERGE:同步作业省了大活

MERGE INTO Sales.Orders AS target USING Staging.OrdersDelta AS source ON target.OrderNo = source.OrderNo WHEN MATCHED THEN UPDATE SET target.Amount = source.Amount WHEN NOT MATCHED THEN INSERT (CustomerID, OrderNo, Amount, Status) VALUES (source.CustomerID, source.OrderNo, source.Amount, source.Status);

我扫代码库,MERGE 出现四十多处。本来最怕这玩意儿,结果全过,一处没改。ETL 作业这块省了我们大概五天活。

存储过程:唯一得人工盯的

简单过程不用动。复杂点的,KDMS 标红哪个改哪个。

@@IDENTITY换LASTVAL(),RAISERROR换RAISE EXCEPTION,动态 SQL 用EXECUTE format(...)重写。老张复核了两周,二十几个高风险过程逐个过,这活没捷径。

割接校验别偷懒

行数一致不算完事,核心表一定做 hash:

SELECT OrderID, MD5(OrderNo || '|' || Amount || '|' || Status) AS h FROM Sales.Orders ORDER BY OrderID LIMIT 1000;

源端和目标端对 hash,对不上就别切。有次差了 3 行,查出来是源库触发器在迁移窗口里又插了数据,KFS 没追到。重跑增量补齐,虚惊一场。

JDBC 驱动换掉:jdbc:kingbase8://192.168.10.60:54321/appdb。MyBatis 基本没动,有个 .NET 老系统用 Dapper,SQL 一个字没改,集成测试直接绿。

最后

三套系统跑下来,应用层改动平均不到 5%。电科金仓这个兼容模式,确实把 TOP、OUTPUT、临时表、联表更新这些脏活咽下去了。存储过程、触发器、复杂事务还是得人一行行看,但工具已经把最重的活扛了。

迁移这事儿,工具解决 80%,剩下 20% 是工程纪律。谁跟你说“一键零改造”,让他拿 650G ERP 跑一遍再说。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进