使用动态SQL

时间:2018-06-04 15:16:32

标签: sql-server azure triggers azure-sql-database dynamic-sql

我们正在构建一个受保护的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兼容性)

0 个答案:

没有答案