SQL Server数据库迁移:V9R4C019 如何接住存量 T-SQL 批处理

做 SQL Server 数据库迁移时,让开发团队紧张的往往不是几百张表,而是仓库里那些跑了很多年的 T-SQL。库存日结用 MERGE 合并数据,账户变更用 OUTPUT 写审计记录,经营报表里还有窗口函数和 PIVOT。如果这几类代码都要重写,后面跟着变的就不只是 SQL,还有异常处理、受影响行数、审计日志和报表字段。

我把这类迁移看成一次"语义对位":不急着按文件数量估工期,先找出最常用、最容易牵动业务的语法块,看目标版本能不能保留原来的表达。V9R4C019 针对 MERGE、DML OUTPUT、窗口函数、PIVOT/UNPIVOT 和 LIKE 通配符等高频 T-SQL 场景继续补强兼容能力,存量代码因此有了尽量保留原有业务语义的条件。

先选对目标版本和兼容模式

SQL Server 代码的语法习惯很明显:TOP、表变量、@name 局部变量、GO 批处理结束符、PRINT、MERGE 和过程中的 BEGIN...END 通常会成组出现。数据迁过去以后再逐条处理,很快就会变成一份没有边界的改造清单。

目标库使用 KingbaseES V9R4C019 SQL Server 兼容版,对象评估、脚本转换和应用回归也要在同一版本上完成。金仓官方的 SQL Server 至 KingbaseES 迁移指南把数据类型、SQL 语法、PL/MSSQL、客户端接口和迁移工具放在一条路径里。这个顺序很实用:先确认代码能否保留,再处理少量确实与应用逻辑相关的差异。

MERGE 保住了日结任务的原有结构

一个典型的库存日结任务,会先把当天变动写入中间表,再按商品和仓库合并到库存主表。原有 T-SQL 可以保持一条 MERGE 的结构:匹配到旧记录就更新数量,没有匹配到就新增。

ini 复制代码
MERGE INTO inventory_balance AS t
USING inventory_delta AS s
ON t.warehouse_id = s.warehouse_id
AND t.product_id   = s.product_id
WHEN MATCHED THEN
    UPDATE SET
        quantity      = t.quantity + s.change_quantity,
        last_batch_no = s.batch_no,
        updated_at    = CURRENT_TIMESTAMP
WHEN NOT MATCHED THEN
    INSERT (
        warehouse_id,
        product_id,
        quantity,
        last_batch_no,
        updated_at
    )
    VALUES (
        s.warehouse_id,
        s.product_id,
        s.change_quantity,
        s.batch_no,
        CURRENT_TIMESTAMP
    );

如果把这段代码拆成"先查询、再判断、最后 UPDATE 或 INSERT",事务中会多出一段不必要的分支。保留 MERGE 后,原来的匹配条件、更新表达式和新增字段仍放在一个语句中,回归测试也能继续围绕批次号、库存量和受影响行数展开。

验收时可以先核对这一批增量是否全部落到主表:

vbnet 复制代码
SELECT d.batch_no,
       COUNT(*) AS delta_rows,
       SUM(d.change_quantity) AS quantity_delta,
       SUM(b.quantity) AS current_quantity
FROM inventory_delta AS d
JOIN inventory_balance AS b
  ON b.warehouse_id = d.warehouse_id
 AND b.product_id   = d.product_id
WHERE d.batch_no = 'DAY-20260901'
GROUP BY d.batch_no;

DML OUTPUT 让审计链不用换写法

金融、会员和供应链系统里,更新数据的同时记录更改前后值是很常见的写法。SQL Server 的 DML OUTPUT 可以从 deleted 和 inserted 中直接取值,再写入审计表。V9R4C019 对这类语法的支持,避免了把原子的一次更新改成应用层的"先读后写"。

sql 复制代码
DECLARE @customer_id bigint = 10012001;
DECLARE @new_limit   numeric(18,2) = 50000.00;
​
UPDATE customer_account
SET credit_limit = @new_limit,
    updated_at   = CURRENT_TIMESTAMP
OUTPUT inserted.customer_id,
       deleted.credit_limit,
       inserted.credit_limit,
       inserted.updated_at
INTO customer_limit_log (
       customer_id,
       old_credit_limit,
       new_credit_limit,
       changed_at
)
WHERE customer_id = @customer_id;

对这段代码做回归,查的不只是账户表中的新额度,还要确认审计表只产生一条对应记录,旧值和新值与业务变更一致:

vbnet 复制代码
SELECT a.customer_id,
       a.credit_limit,
       l.old_credit_limit,
       l.new_credit_limit,
       l.changed_at
FROM customer_account AS a
JOIN customer_limit_log AS l
  ON l.customer_id = a.customer_id
WHERE a.customer_id = 10012001
ORDER BY l.changed_at DESC;

窗口函数和 PIVOT 继续服务于原报表

报表 SQL 的改造风险往往比普通查询更高。一方面,报表字段已经被前端、导出模板和定时任务使用;另一方面,同一统计口径常常同时用到分组排名和行列转换。

例如,这段 SQL 用 ROW_NUMBER() 找每个仓库最后一次盘点结果:

sql 复制代码
WITH latest_stocktake AS (
    SELECT warehouse_id,
           product_id,
           stocktake_at,
           actual_quantity,
           ROW_NUMBER() OVER (
               PARTITION BY warehouse_id, product_id
               ORDER BY stocktake_at DESC, stocktake_id DESC
           ) AS rn
    FROM stocktake_record
)
SELECT warehouse_id,
       product_id,
       stocktake_at,
       actual_quantity
FROM latest_stocktake
WHERE rn = 1;

排序条件里除了 stocktake_at,还加上唯一的 stocktake_id。同一时刻有多条盘点数据时,结果仍然有确定顺序,源库和目标库才能稳定对比。

月度报表原来使用 PIVOT 把季度销售额展开为四列,迁移后也可以保留原来的列名和结果结构:

css 复制代码
SELECT warehouse_id,
       [Q1],
       [Q2],
       [Q3],
       [Q4]
FROM (
    SELECT warehouse_id,
           quarter_code,
           sales_amount
    FROM warehouse_quarter_sales
) AS src
PIVOT (
    SUM(sales_amount)
    FOR quarter_code IN ([Q1], [Q2], [Q3], [Q4])
) AS p;

前端报表仍然接收 Q1 到 Q4 四个字段,导出模板和图表配置也就不需要因为数据库切换而调整。UNPIVOT 用于反向将多个季度列还原成行,在导入标准明细表或者重新汇总时同样实用。

LIKE 里的字符范围不再另写正则

SQL Server 的 LIKE 除了 % 和 _,存量代码里还经常能看到方括号字符范围。仓储系统用代码规则筛选 A 到 C 类物料时,可能就是这样一句:

vbnet 复制代码
SELECT material_code,
       material_name,
       warehouse_id
FROM material_master
WHERE material_code LIKE '[A-C][0-9][0-9]%'
ORDER BY material_code;

V9R4C019 对 SQL Server LIKE 通配符的完整支持,让这类搜索条件可以继续保留。项目组只需要把代码中用到的字符集、排序规则和样本结果放进回归清单,不用为每条范围匹配另造函数。

改动从"全面重写"收敛到连接和回归

一轮 T-SQL 梳理做完后,代码可以按处理方式分成三组:原样保留、工具自动转换、少量人工复核。MERGE、DML OUTPUT、窗口函数、PIVOT/UNPIVOT 和 SQL Server 风格的 LIKE 通配符可以进入第一组,开发人员把时间留给真正需要确认的业务对象。

上线前的回归也不用从头设计。库存日结核对批次和数量,账户额度核对主表与审计记录,窗口函数核对每组第一条,PIVOT 核对行数、四个季度列和合计值,LIKE 核对边界字符。这些都是原系统已经在用的业务语义,只需要在 V9R4C019 上重新确认一遍。

迁移计划也因此更容易排。数据库对象和存量 T-SQL 保留原结构,应用侧把改动集中在驱动、连接配置和必要的差异项上。开发团队面对的不再是一句"所有 SQL 都要改",而是一份已经按语法块分好类、能够逐项回归的迁移清单。

相关推荐
JosieBook5 小时前
【WinForm 代码反脆弱系列】04 数据库操作 —— 连接字符串、连接对象与连接池
数据库·oracle
小蒜学长5 小时前
基于SpringBoot+Vue的小学数学智能出题系统(代码+数据库+LW)
java·数据库·spring boot·后端·智能出题系统
71-35 小时前
MySQL密码重置
数据库·笔记·学习·mysql
锅里游的鱼吖6 小时前
echarts自定义折线图
前端·javascript·数据库
酷虎软件6 小时前
视频加标题字幕 API 接口文档
java·数据库·mysql
夜雪一千6 小时前
MySQL 触发器有哪些应用场景?从入门场景到避坑实战
数据库·mysql
lusklusklusk6 小时前
Oracle数据库基础之6_外部表临时表分区表_物化视图_约束的介绍
数据库·oracle
智商偏低7 小时前
【无标题】
服务器·数据库·oracle
雨落在了我的手上7 小时前
MySQL数据库基础(1):MySQL的安装-Windows
数据库·mysql
涉密IT资质笔记7 小时前
涉密人员脱密期管理规范:期限分级模型、就业限制边界与违规认定标准
java·服务器·前端·网络·数据库