首页 > 数据库 >SQLServer递归触发器在KES中的一次改造分析

SQLServer递归触发器在KES中的一次改造分析

时间:2023-09-18 14:57:53浏览次数:37  
标签:触发器 递归 tt1 SQLServer TRIGGER KES id delete

文章概要:

某项目将数据从 SQLSERVER 迁移到 KES。其中SQLSERVER中触发器用到了 TRIGGER_NESTLEVEL() 函数,KES并不能直接支持该函数。

起初在分析该问题时想复杂了本文做了一次记录。实际上在kes兼容sqlsevrer基础语法,直接简单使用SYS_TRIGGER_DEPTH()替换 TRIGGER_NESTLEVEL() 函数即可,用来判断递归层级实测是等价的(如果只是想知道最终解决方案,读到这里就可以了)。

本文将主要介绍该函数结合客户代码的替代改造分析过程和解决方案验证。

一,TRIGGER_NESTLEVEL() 函数的作用是撒?

1,查官网的解释:返回为激发触发器的语句执行的次数。 TRIGGER_NESTLEVEL 在 DML 和 DDL 触发器中用以确定当前的嵌套层数。
官网链接:https://learn.microsoft.com/zh-cn/sql/t-sql/functions/trigger-nestlevel-transact-sql?view=sql-server-ver16

2,同时也翻阅了一下百度,对于DDL触发器而言,一般都是用于防止触发器一直触发,例如触发器里面执行数据DELETE,
再触发当前触发器,从而导致进入循环,所以要如果当前触发内会对表再执行DELETE,
需要添加对TRIGGER_NESTLEVEL的检查,防止触发器嵌套层数太多。

3,并且对于触发器本身而言,需要参数RECURSIVE_TRIGGERS开启后触发器才能递归(默认情况下不递归,这也是在测试验证过程发现的)

官网链接:https://learn.microsoft.com/zh-cn/sql/relational-databases/triggers/create-nested-triggers?view=sql-server-ver16

这是客户代码的sqlserver代码的简化案例:

create trigger "tgr_delete_name" 
on "dbo".tbl_xxx_name 
for delete 
as
declare @id int
begin
	select @id =id from deleted
	if ( (select trigger_nestlevel() ) <30  and @@rowcount >0)
	begin
		delete from budg_code_item where super_id = @id
	end
end;

触发器本身而言逻辑很简单,是一个delete事件触发器,作用于tbl_xxx_name表,在满足递归级别为30以内时,且存在数据被删除(@@rowcount >0)的条件时,又在触发器体内对tbl_xxx_name表执行delete,从而形成递归,
trigger_nestlevel的作用就是限制递归层数,如果没有这条IF语句则会死循环下去。

二,TRIGGER_NESTLEVEL() 函数的改造分析

那么现在的问题是,KES支持这么玩吗?KES 中的递归检测又是什么?

KES支持触发器递归:

在PL/SQL中,触发器可以形成递归。递归触发器是指一个触发器在执行期间触发了另一个相同类型的触发器。这种情况可能会导致无限循环和性能问题。
为了避免或者限制递归触发器,查阅一些资料,可以使用以下方法:

1)禁用触发器:在触发器中添加条件,只有当满足某些条件时才执行触发器的操作。这样可以避免触发器在执行期间再次触发自己。

2)使用标志变量:在触发器中使用一个标志变量来标记触发器是否已经执行过。如果触发器已经执行过,则不再执行触发器的操作。

3)调整触发器顺序:如果有多个触发器与同一表相关联,可以调整它们的执行顺序,确保递归触发器不会发生。

4)重新设计触发器:如果递归触发器无法避免,可能需要重新设计数据库模型或修改触发器逻辑,以避免递归触发器的情况。

需要注意的是,递归触发器可能会导致性能问题和无限循环,因此在设计和使用触发器时需要小心处理,确保其行为可控和可预测。

一开始思考改造问题时,觉得第二条应该可以(或者1和2结合),但是一细想当存在各种情况的并发时,该方法就无法满足了。

3和4也不能满足,因为无法得到递归层级,因此上述方法都不可取。

但是,但是,实际上上面的考虑想复杂了,KES支持sys_trigger_depth()函数来判断触发器的递归

三,TRIGGER_NESTLEVEL() 函数的改造结果及其验证

create table tt1(id int , supid int , na varchar(10));	

insert into tt1 values
( 1 , 1 , 'a'),
(11 , 1 , 'a11'),
(12 , 11 , 'a12'),
(111 ,12, 'b111'),
(112 ,111, 'b12'),
(121 ,122, 'c21'),
(122 ,121, 'c22');

create trigger "tgr_delete_name"     ---kes的POC分支上支持该语法,可以实现sqlserevr基本语法的兼容
on tt1 
for delete 
as
declare @id int
begin
	select @id = id from deleted
	if ( (select sys_trigger_depth() ) < 4 and  @@rowcount > 0 )
	begin
		delete from tt1 where supid = @id
	end
end;

delete from tt1 where id = 1;

select * from tt1;

test-# delete from tt1 where id = 1;
test-# /
DELETE 1
test-# select * from tt1;
test-# /
id  | supid | na
----+-------+-----
112	| 111	| b12
121	| 122	| c21
122	| 121	| c22
(3 rows)

sqlserver运行验证:

USE master;  
GO  
ALTER DATABASE master SET RECURSIVE_TRIGGERS ON;  
GO  
drop table tt1;
go
create table tt1(id int , supid int , na varchar(10));	
go
insert into tt1 values
( 1 , 1 , 'a'),
(11 , 1 , 'a11'),
(12 , 11 , 'a12'),
(111 ,12, 'b111'),
(112 ,111, 'b12'),
(121 ,122, 'c21'),
(122 ,121, 'c22');
go

create trigger tgr_delete_name
on tt1 
for delete 
as
declare @id int
begin
	select @id =id from deleted
	if ( (select trigger_nestlevel() ) < 4  and @@rowcount >0)
	begin
		delete from tt1 where supid = @id
	end
end;
go
delete from tt1 where id = 1;

select * from tt1;

--运行结果

112	111	b12
121	122	c21
122	121	c22

其余验证不再累述,结果一致,改造完毕。

改造小结

实际上,查微软的官网来看TRIGGER_NESTLEVEL()函数的功能要强大一些,但是仅就判触发器递归层级而言,sys_trigger_depth()可以直接替换 TRIGGER_NESTLEVEL() 函数。

另一种思考,基于客户递归删除数据的逻辑,是否可以使用如下WITH RECURSIVE AS语句递归删除?该语句也是能控制递归层数的,不过暂未尝试过。

WITH RECURSIVE CTE_NAME AS (
初始语句(非递归部分)
UNION ALL
递归部分语句
)
[ SELECT| INSERT | UPDATE | DELETE]

标签:触发器,递归,tt1,SQLServer,TRIGGER,KES,id,delete
From: https://www.cnblogs.com/kingbase/p/17711828.html

相关文章

  • Navicat连接SQLServer提示:未发现数据源名并且未指定默认驱动程序
    问题:Navicat连接SQLServer提示:未发现数据源名并且未指定默认驱动程序解决方式一:可以直接安装Navicat根目录(安装目录)下的sqlncli.msi(32位)或sqlncli_x64.msi(64位) 双击开始安装,一直下一步,正常安装完成的话,重启下Navicat就没有问题了,可以正常连接了。解决方式一:在官......
  • 今天安装了SqlServer2005
    手头早就有了一套sqlserver2005,今天上午看了一会儿《sap德国造》,有些累了。想歇息一会儿。在随便浏览电脑的时候,无疑中看到了存储在硬盘上的sqlserver2005安装程序。就猛地萌生了安装它的想法。 虽然sqlserver2005与2000已经区别很大了。但是基于桌面操作系统的应用程序的安装工作......
  • 怎样在触发器中删除刚刚录入但是不合法的记录?
    建立一个临时表:CREATEGLOBALTEMPORARYTABLEnorthsnow_tmp(northsnow_idvarchar2(20))ONCOMMITDELETEROWS;在业务表上创建一个行级触发器:createorreplacetriggertrg_northsnowafterinsertontb_northsnowforeachrow......
  • 触发器中调用远程链接的数据库的配置方法
    一般我们要保证两台机器可以互通。并且两台机器上都安装并启动了msdtc.当然不是安装并启动了msdtc就可以的。我们可能还要做一些配置。1、运行regedt32,浏览至HKEY_LOCAL_MACHINEoftware/Microsoft/MSDTC。添加一个DWORD值TurnOffRpcSecurity,值数据为1。2、重启MSDTC服务。3......
  • sqlserver存储过程报错:当前事务无法提交,而且无法支持写入日志文件的操作。请回滚该事
    ​ ​编辑 现象:系统出现异常,手动执行过程提示如上。  问题排查:1.直接执行的过程事务挂起(排除)2.重启数据库实例(重启后无效)3.过程中套用过程,套用的过程中使用事务,因为插入的表结构字段超出最大长度(修改表结构问题恢复) 结论:事务执行失败,嵌套层级太深。  ​......
  • Asp.net的项目SqlServer数据库迁移到MySql
    1、环境Windows10+VS2015+.Net4.5.2+MySql5.72、准备   1)、NavicatPremium15(用于数据库转换,其它版本的亦可)   2)、Vs2015   3)、安装mysql-connector-net-6.9.9.msi   4)、安装mysql-for-visualstudio-1.2.6.msi3、数据库转换  1)、打开nav......
  • SQLServer游标的使用,读取表数据赋值循环
    --声明一个游标--定义一个叫Cursor_temp的游标,存放FORSELECT后的数据DECLARECursor_tempCURSORFORSELECTId,CodeFROMTableName--打开一个游标OPENCursor_tempDECLARE@IDuniqueidentifier,@Codenvarchar(5......
  • ​​Pyflakes
    Pyflakes是Python的一个分析包,可用来分析代码,发现各种潜在的问题,例如:引入但没有用到的模块,定义了但后面没有用到的变量等。使用Pyflakes分析代码风格和寻找违法PEP-8编码规范的地方是相当简单的;Pyflakes的另一优势是分析速度快,但它能报告的错误类型是相当有限的123。1:【脚本语言......
  • sqlserver中字符串替换其中几个字为打码
    select*fromdocbasewhereisdelete=0updatedocbasesetsubject=STUFF(subject,23,4,'****')whereisdelete=0 updatedocbasesetsubject=STUFF(subject,23,6,'******')whereisdelete=0andLEN(subject)>19STUFF函数也可以用来替换指定的字符串。它的定义如下......
  • SQLServer数据库DBCC CHECKIDENT命令介绍
    在SQLServer输入数据时,碰到有主键ID需要维护时,可使用IDENT_CURRENT和CHECKIDENT命令来维护。 IDENT_CURRENT命令返回指定表或视图生成的最后一个标识值。用法如下:      IDENT_CURRENT('table_name') 返回类型numeric(38,0).  DBCC CHECKIDENT命令用于检查或......