我们正在构建一个受保护的INSTEAD OF UPDATE触发器来监视和控制几个表的更新(系统的大多数MasterData表,大约150个)。因此,尽可能地(安装,更新)我们尝试尽可能重用代码(没有硬编码字段名称或表名)。
控制"实际"如果是行的版本,则存在_ACTIVE字段,并且每个新版本的这个字段都会减少(ACTIVE行获取ACTIVE = 1)。抱歉,由于向后兼容性,我们无法使用时态表功能(基于此功能构建了大量业务逻辑)
更新逻辑包括在它们影响表之前处理OLD和NEW行,并且一旦处理完所有内容,就更新表;不仅受影响的行,而且所有具有相同唯一性的键字段(唯一性字段的标识也旨在动态完成;在下面的示例中,where子句在变量@toWhereOnClause上动态构造)
"真实" table遇到两个动作,首先插入一堆新行,其中_ACTIVE = 2,其次,需要更新的所有行获取_ACTIVE - = 1,将行的最新版本设置为1
问题出现了,因为需要动态创建第二个动作,即更新,以避免输入表名,并手动设置@toWhereOnClause。这又触发了TRIGGER,因为它是动态SQL(我们相信)并未被TRIGGER_NESTLEVEL()= 1
捕获代码结构如下:
CREATE OR ALTER TRIGGER [schema].[triggerName] ON [schema].table
INSTEAD OF UPDATE
AS
BEGIN
SET @tableName = '[schema].[table]' // only line to modify for diferent tables
//TRIGGER PREPARATION
SET @schema = (SELECT SUBSTRING(@tableName, 1, (CHARINDEX('].', @tableName))))
SET @table = (SELECT SUBSTRING(@tableName, (CHARINDEX('[', @tableName, 3)), LEN(@tableName)))
SET @fieldNameS = (SELECT + ',' + QUOTENAME(COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @schema
AND TABLE_NAME = @table
ORDER BY ORDINAL_POSITION
FOR XML path(''));
SET @uniqueFieldSelector = (SELECT +' AND LeftTable.'+ COLUMN_NAME + ' = #INSERTED.' + COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = @tableName
AND COLUMN_NAME NOT LIKE '%Active%'
FOR XML PATH(''))
SET @toWhereOnClause = (SELECT (SUBSTRING(@uniqueFieldSelector, 5, LEN(@uniqueFieldSelector))))
// DUPLICATE TRIGGER TABLES INTO TEMP TABLE TO WORK ON NEW AND OLD LINES
SELECT * INTO #INSERTED FROM INSERTED -- Can't modify logic table values (INSERTED), we put it in a temp table
SELECT * INTO #DELETED FROM DELETED
// SEVERAL INSTRUCTIONS TO TREAT THE OLD AND NEW LINES (not shown here) AND CALCULATE IF THE UPDATE IS LEGAL (@CONTINUE_TRIGGER)
...
// REAL UPDATE
IF TRIGGER_NESTLEVEL() = 1 AND @CONTINUE_TRIGGER = TRUE
--https://stackoverflow.com/questions/1529412/how-do-i-prevent-a-database-trigger-from-recursing
BEGIN
SET @statementINSERT = N'INSERT INTO' + @tableName + '( ... )
SELECT ... FROM #INSERTED ';
EXECUTE sp_executesql @statementINSERT
SET @statementUPDATE = N'UPDATE TheRealTable
SET TheRealTable._ACTIVE -= 1
FROM ' + @tableName + ' AS TheRealTable
INNER JOIN #INSERTED ON ' + @toWhereOnClause;
EXECUTE sp_executesql @statementUPDATE
END
END
是的,我们知道它很复杂,但遗产并没有提供很多选择。
SO:
有没有办法再次避免使用dynamicSQL触发TRIGGER?
(系统在WindowsServer和Azure实例上运行,至少都具有120兼容性)