SQL database project 如何在 build 时检查 insert 列兼容性?
结论
示例中将 VARCHAR(MAX) 写入 VARCHAR(20) 的操作,不会仅因源列的声明长度大于目标列,就在 SQL database project 的常规构建检查中报错。项目也没有内置的通用开关,能将这类映射统一标记为错误或警告。
要在 build 阶段拦截这类操作,需要添加自定义静态分析规则或构建检查。要确认实际数据能否插入,则需要运行数据库测试。
为什么 build 没有报错?
VARCHAR(MAX) 和 VARCHAR(20) 之间允许转换。源列的声明长度较大,说明它可能保存超过目标容量的数据,但并非每一行都会超长。
例如,源列中的值是 'abc',就可以正常插入 VARCHAR(20);如果值是连续的 21 个 ASCII 字符,就会超出目标列容量。
SQL database project 在构建时会验证数据库对象模型,检查语法、对象引用等,但不会执行存储过程,也不会读取 Table1 的实际数据。因此,示例中的存储过程可以通过构建,执行时仍可能发生字符串截断错误。
如何在 build 阶段检查?
如果团队要求所有从较宽字符串列写入较窄字符串列的操作都必须告警,可以编写静态分析规则,并接入构建流程:
- 解析存储过程中的
INSERT ... SELECT。 - 按位置匹配目标列列表和
SELECT输出表达式。 - 从数据库模型中获取源列、目标列的类型及长度。
- 对可能缩短字符串容量的映射输出诊断,并根据项目要求让构建失败。
分析可以使用 DacFx 数据库模型配合 ScriptDom 实现,具体扩展方式取决于所用的 SQL database project 工具链。
规则还需要分析表达式,例如:
INSERT dbo.Table2 (col1)
SELECT LEFT(col1, 20)
FROM dbo.Table1;
这里写入的是 LEFT 的结果,只比较两张表的列定义无法判断风险。别名、视图、函数和动态 SQL 也会增加分析难度。这类规则通常报告的是潜在截断风险,每次告警并不一定都对应实际执行错误。
仅开启内置代码分析或“将警告视为错误”,不会自动增加这项检查,前提是已有规则能产生相应诊断。发布时的数据丢失检查也不能替代存储过程内部的列映射检查。
如何验证实际插入行为?
可以在测试数据库中运行以下示例,验证超长数据插入时的行为:
SET ANSI_WARNINGS ON;
CREATE TABLE #Table1
(
col1 VARCHAR(MAX)
);
CREATE TABLE #Table2
(
col1 VARCHAR(20)
);
INSERT INTO #Table1 (col1)
VALUES (REPLICATE('A', 21));
INSERT INTO #Table2 (col1)
SELECT col1
FROM #Table1;
在 ANSI_WARNINGS ON 的条件下,最后一条语句会因字符串超出目标列容量而报错。错误消息的详细程度取决于 SQL Server 版本及相关配置。
这项检查发生在执行期。要在持续集成流程中发现问题,可以先构建项目,再部署到测试数据库,准备边界数据并执行实际存储过程。测试至少应覆盖恰好达到容量上限和超过上限的值。
数据应该如何处理?
处理方式取决于业务要求:
- 如果目标列需要完整保存源数据,就调整目标列容量。
- 如果所有值都必须符合较小容量,就在数据入口增加校验,或统一相关列的定义。
- 如果业务允许截断,就明确编写截断逻辑,并测试截断后的结果。
不要为了通过检查而添加下面的转换:
INSERT dbo.Table2 (col1)
SELECT CAST(col1 AS VARCHAR(20))
FROM dbo.Table1;
显式转换可能提前截断字符串,导致插入成功,但原始内容已经丢失。
VARCHAR(20) 的容量是 20 字节,不一定能容纳 20 个字符。涉及多字节编码时,需要根据目标列的编码和排序规则判断容量。排查当前数据可以使用 DATALENGTH;如果源列与目标列之间存在编码转换,还需要考虑转换后的字节数。
备注:内容仅供参考。
