SQLServer递归触发器在KES中的一次改造分析
文章概要:
某项目将数据从 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开启后触发器才能递归(默认情况下不递归,这也是在测试验证过程发现的)
这是客户代码的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]
SQLServer递归触发器在KES中的一次改造分析的更多相关文章
- SQLServer之触发器简介
触发器定义 触发器是数据库服务器中发生事件时自动执行的一种特殊存储过程.SQLServer允许为任何特定语句创建多个触发器.它的执行不是由程序调用,也不是手工启动,而是由事件来触发,当对数据库进行操作 ...
- sqlserver数据库触发器调用外部exe
sqlserver数据库触发器调用外部exe,同事可以选择参入参数! sqlserver使用 master..xp_cmdshell 进行外部exe的执行. 使用master..xp_cmdshell ...
- 在SQLServer使用触发器实现数据完整性
1.实现数据完整性的手段 在sqlserver中,在服务器端实现数据完整性主要有两种手段:一种是在创建表时定义数据完整性,主要分为:实体完整性.域完整性.和级联参照完整性:实现的手段是创建主键约束.唯 ...
- 分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)
分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...
- (转)分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)
分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...
- SqlServer添加触发器死锁的原因
之前遇到过SqlServer添加触发器死锁的情况,纠结了很长时间 最近发现原来是因为我在建表的时候,把id设成主键后,系统默认了加一个聚集的索引 就是聚集索引把表锁住了
- java基础 File 递归删除文件夹中所有文件文件夹 目录(包含子目录)下的.java文件复制到e:/abc文件夹中, 并统计java文件的个数
File 递归删除文件夹中所有文件文件夹 package com.swift.kuozhan; import java.io.File; import java.util.Scanner; /*键盘录 ...
- day18 时间:time:,日历:calendar,可以运算的时间:datatime,系统:sys, 操作系统:os,系统路径操作:os.path,跨文件夹移动文件,递归删除的思路,递归遍历打印目标路径中所有的txt文件,项目开发周期
复习 ''' 1.跨文件夹导包 - 不用考虑包的情况下直接导入文件夹(包)下的具体模块 2.__name__: py自执行 '__main__' | py被导入执行 '模块名' 3.包:一系列模块的集 ...
- C中二叉排序树的非递归和递归插入操作以及中序遍历代码实现【可运行】
C中二叉排序树的非递归和递归插入操作以及中序遍历代码实现[可运行] #include <stdio.h> #include <stdlib.h> typedef int Key ...
- 城市经纬度 json 理解SignalR Main(string[] args)之args传递的几种方式 串口编程之端口 多线程详细介绍 递归一个List<T>,可自己根据需要改造为通用型。 Sql 优化解决方案
城市经纬度 json https://www.cnblogs.com/innershare/p/10723968.html 理解SignalR ASP .NET SignalR 是一个ASP .NET ...
随机推荐
- Js中Proxy对象
Js中Proxy对象 Proxy对象用于定义基本操作的自定义行为,例如属性查找.赋值.枚举.函数调用等. 语法 const proxy = new Proxy(target, handler); ta ...
- ffmpeg之avformat_alloc_output_context2
函数原型: int avformat_alloc_output_context2(AVFormatContext **ctx, const AVOutputFormat *oformat, const ...
- powerdesigner自定义实体显示的属性
我做概要设计的时候需要画实体的逻辑模型图,默认的时候是这样的: 但是我想只保留属性名,隐藏数据类型和下面的横线怎么办?效果如下: 按以下操作即可实现:
- win32 - 计算位图所需的字节总数
BITMAPINFOHEADER文档详细介绍了所需要的步骤, 对于未压缩的RGB格式,最小跨度始终是图像宽度(以字节为单位),四舍五入到最接近的DWORD.可以使用以下公式来计算步幅: stride ...
- 【Android 逆向】frida 检测绕过
1. aaa.apk 安装到手机,是一个叫玩吧的应用 ./hooker ...... 23248 浏 览 器 com.browser2345_oem 32541 玩吧 com.wodi.who 244 ...
- 【Azure Redis 缓存】Redis连接无法建立问题的排查(注:Azure Redis集成在VNET中)
问题描述 在Azure App Service中部署的应用,需要连接到Redis中,目标Redis已经集成了虚拟网络(VNET)并且在Redis的网络防火墙中已经添加App Service的出站IP地 ...
- 【Azure API 管理】APIM不能连接到 App Service (APIM cannot connect to APP service)
问题描述 APIM 无法正确连接到App Service,返回500错误: { "statusCode": 500, "message": "Inte ...
- Nebula Graph 源码解读系列 | Vol.04 基于 RBO 的 Optimizer 实现
上篇我们讲述了一个执行计划是如何生成的,这次我们来看下这个生成的执行计划是被 Optimizer 优化的. 概述 Optimizer,优化器,顾名思义就是一个用来优化执行计划的组件.数据库的优化器通常 ...
- element_ui实现表格内套表单,点击可以编辑
<template> <div class="app-container"> <el-table :data="list" str ...
- redis开启多端口
Centos安装多端口的redis服务 背景 redis默认端口6379,由于开发需要,key有重复.于是另起端口6380. 配置服务过程 1.新建/etc/redis6380.conf,内容如下: ...