ODBC SQL 服务器驱动程序 - 在 recordset.UpdateBatch 方法上“44444”错误附近的语法不正确

ODBC SQL Server Driver - Incorrect syntax near '44444' error - on recordset.UpdateBatch method

VB.Net windows 表单程序在 "production" 环境中正常运行(即 .EXE 在 Windows 2008R2 服务器上运行,连接到 SQL另一台服务器上的 Server 2008 数据库)。

该平台被克隆到 "test" 环境(即托管在 AWS 上;数据库服务器已由管理过渡到 SQL Server 2016 的系统管理员升级到AWS).

问题:在 AWS 上测试的这个错误是否指向某种类型的 SQL 2016 配置问题?

代码片段如下:

Dim cn As New ADODB.Connection()
cn.ConnectionString = BuildConnectStringODBC()
cn.Open()
rs = New Recordset
rs.CursorLocation = ADODB.CursorLocationEnum.adUseClient
rs.CursorType = ADODB.CursorTypeEnum.adOpenKeyset
rs.LockType = ADODB.LockTypeEnum.adLockBatchOptimistic
rs.Open("ImportTempTable", cn)
rs.AddNew()
rs.Fields("StateCD").Value = CRMortgage.StateCD
rs.Fields("CountyCD").Value = CRMortgage.CountyCD                
' etc.
rs.UpdateBatch()

单步调试表明,在 rs 对象上的所有上述方法都可以正常工作,直到 rs.UpdateBatch 失败并出现 "incorrect syntax" 错误。

这个程序在 "production" 和系统管理员中多次执行没有问题,我怀疑 SQL 服务器配置或选项有问题,但我们不确定从哪里开始。

编辑更新:

这是来自 SQL Mgmt Studio 的 ImportTempTable 的脚本模式:

CREATE TABLE [dbo].[ImportTempTable](
    [StateCD] [varchar](2) NULL,
    [CountyCD] [varchar](3) NULL,
    [SaleMnYear] [datetime] NULL,
    [LenderName] [varchar](40) NULL,
    [MDSID] [varchar](6) NULL,
    [Amt_PUR] [int] NULL,
    [Nbr_MTG] [int] NULL,
    [Amt_MTG] [int] NULL,
    [Nbr_JUMBO] [int] NULL,
    [Amt_JUMBO] [int] NULL,
    [Nbr_GOVT] [int] NULL,
    [Amt_GOVT] [int] NULL,
    [Nbr_Fixed] [int] NULL,
    [Nbr_ARMS] [int] NULL,
    [Nbr_NewConst] [int] NULL,
    [Amt_Fixed] [int] NULL,
    [Amt_ARMS] [int] NULL,
    [Amt_NewConst] [int] NULL,
    [LTV] [real] NULL,
    [Nbr_Below80] [int] NULL,
    [Nbr_80_85] [int] NULL,
    [Nbr_85_90] [int] NULL,
    [Nbr_Above90] [int] NULL,
    [LoanType] [varchar](1) NULL,
    [SaleType] [varchar](1) NULL,
    [RateType] [varchar](1) NULL,
    [CondoCode] [varchar](1) NULL,
    [Zip] [varchar](5) NULL,
    [ZipSuffix] [varchar](4) NULL,
    [StateAbbr] [varchar](2) NULL,
    [DateImported] [datetime] NULL,
    [RecordType] [varchar](1) NULL
) ON [PRIMARY]

经过调查,我确实发现了两个与上述相关的触发器table:

ALTER TRIGGER [dbo].[ImportTempTable_ITrig] ON [dbo].[ImportTempTable] FOR INSERT AS
/*
 * PREVENT NULL VALUES IN 'SaleMnYear'
 */
IF (SELECT Count(*) FROM inserted WHERE SaleMnYear IS NULL) > 0
    BEGIN
        RAISERROR 44444 'Field ''SaleMnYear'' cannot contain a null value.'
        ROLLBACK TRANSACTION
    END
ELSE 
/*
 * PREVENT NULL VALUES IN 'LenderName'
 */
IF (SELECT Count(*) FROM inserted WHERE LenderName IS NULL) > 0
    BEGIN
        RAISERROR 44444 'Field ''LenderName'' cannot contain a null value.'
        ROLLBACK TRANSACTION
    END

ALTER TRIGGER [dbo].[ImportTempTable_UTrig] ON [dbo].[ImportTempTable] FOR UPDATE AS
/*
 * PREVENT NULL VALUES IN 'SaleMnYear'
 */
IF (SELECT Count(*) FROM inserted WHERE SaleMnYear IS NULL) > 0
    BEGIN
        RAISERROR 44444 'Field ''SaleMnYear'' cannot contain a null value.'
        ROLLBACK TRANSACTION
    END
ELSE 
/*
 * PREVENT NULL VALUES IN 'LenderName'
 */
IF (SELECT Count(*) FROM inserted WHERE LenderName IS NULL) > 0
    BEGIN
        RAISERROR 44444 'Field ''LenderName'' cannot contain a null value.'
        ROLLBACK TRANSACTION
    END

我不知道这些触发器已经到位。我会重新思考他们存在的理由。也许这是多年来我们第一次得到 "bad data" 并因此引发了这个愚蠢的错误。我认为上面列定义的 NULL 属性将允许 NULL;这种触发方法似乎是不明智的。

SQL 中的实例级别是否有可以启用或禁用触发器的东西?我觉得不是...

更多的研究应该可以解决这个问题。

Incorrect syntax near '44444' 错误表示 SQL 触发器使用未记录的 RAISERROR 语法版本,最高支持 SQL Server 2008 R2。从 SQL Server 2012 开始已经不支持未记录的 RAISERROR 语法,因此您需要使用支持的 RAISERROR syntax or use THROW 作为替代:

-- RAISERROR example
RAISERROR(44444, -1, -1, 'Field ''SaleMnYear'' cannot contain a null value.');

-- THROW example
THROW 44444, 'Field ''SaleMnYear'' cannot contain a null value.', 1;

此外,您可以尝试从 ImportTempTable 中删除 ImportTempTable_ITrigImportTempTable_UTrig 触发器,然后将列 SaleMnYearLenderName 设置为不可空数据类型,从较新的 SQL 服务器版本开始,不需要触发器来检查空值。

DROP TRIGGER IF EXISTS [dbo].[ImportTempTable_ITrig]
DROP TRIGGER IF EXISTS [dbo].[ImportTempTable_UTrig]

-- if the table already contains null value, update them first
UPDATE [dbo].[ImportTempTable] SET SaleMnYear = {value}, LenderName = {value} 
WHERE SaleMnYear IS NULL {AND/OR} LenderName IS NULL

ALTER TABLE [dbo].[ImportTempTable] ALTER COLUMN SaleMnYear {data_type} NOT NULL
ALTER TABLE [dbo].[ImportTempTable] ALTER COLUMN LenderName {data_type} NOT NULL

截至 RecordSet.UpdateBatch 因上述错误而停止,它假设 运行 ADO 更新进程试图将空值添加到 SaleMnYearLenderName通过 if-condition 触发器设置为不允许空值的列(确保源数据列中的记录不包含空值)。

注意:我建议使用托管的 SqlClient.SqlConnection 而不是 ODBC 的 ADODB.Connection 来处理 SQL 服务器连接实例。

参考:

Differences between RAISERROR and THROW in SQL Server

类似问题:

Migrating from SQL Server 2008r2 to SQL Sever 2012

RAISERROR issue since migration to SQL Server 2012