欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

在论坛中出现的比较难的sql问题:20(触发器专题2)

程序员文章站 2022-05-04 09:03:39
...

最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了。 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路。 本篇是触发器专题,有很多触发器的问题。 1、关于触发

最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了。

所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路。

本篇是触发器专题,有很多触发器的问题。

1、关于触发器中回滚的问题。

在官方文档http://technet.microsoft.com/zh-cn/library/ms181299.aspx中“互操作性”段落中有这么一句:
触发器继续执行 ROLLBACK 语句之后的所有其余语句。 如果这些语句中的任意语句修改数据,则不回滚这些修改。 执行其余的语句不会激发嵌套触发器。
其中“执行其余的语句不会激发嵌套触发器”,是否意味着在回滚操作后面的语句如果继续执行的话,后面语句触发的触发器就不会执行了。比如后面的语句有针对一个表的update操作,而这张表又有update的触发器,这个时候update会执行成功,但触发器不会执行?

但是很奇怪,我做了一个实验,发现,即使是嵌套触发器,也能够被触发,和微软文档上说的不一样。

从下面的insert时的输出,可以看出,在触发器中rollback之后的语句,是可以执行的,另外,update另一个表的操作也执行成功,并且触发了触发器。

--drop table t1
--drop table t2

create table t1(id int)
create table t2(id int)

insert into t2
values(100)
go

create trigger dbo.trigger_t1
on t1
for insert
as

rollback;

select '这是rollback之后的语句,这里能执行'

update t2
set id = 1;
go

create trigger dbo.trigger_t2
on t2
for update
as

select '这是t2的update触发器,这里能执行'

go

--插入数据
insert into t1
values(1)
/*
这是rollback之后的语句,这里能执行

这是t2的update触发器,这里能执行

消息 3609,级别 16,状态 1,第 3 行
事务在触发器中结束。批处理已中止。
*/ 

--没有记录
select * from t1


select * from t2
/*
id
1
*/

2、触发器的错误处理

http://bbs.csdn.net/topics/390637035

我在A表写了个触发器 插入后执行,是向B表插入一条记录

USE [test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tba_delete] 
   ON  [dbo].[tba] 
   AFTER INSERT
AS 
BEGIN
	SET NOCOUNT ON;
	insert into test.dbo.tbb(title,info) values('title','info');
END
在A表插入数据后正常。当我把 语言改为:
insert into test.dbo.tbb(id,title,info) values(1,'title','info');
故意出错。问题出现了,当触发器内的语句出错,向A表插入的数据就不能成功插入。
向朋友们求教个问题。我要如何把触发器中的出错信息记录下来?或者在什么地方可以查看到触发器执行时是否出错了,及查看错误的信息?

我的解法:

USE [test]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

create table [tba](title varchar(20),info varchar(20))

create table tbb(title varchar(20),info varchar(20))


--创建一个存放错误信息的表
create table tb_error_message
(
obj_name nvarchar(30),
obj_type nvarchar(15),
err_msg nvarchar(100),
err_date datetime
)
go

drop trigger tba_delete
go

create TRIGGER [dbo].[tba_delete]
ON [dbo].[tba]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;

begin try
--这里模拟插入title列的数据长度,超过了定义时的20
insert into test.dbo.tbb(title,info) values('title1111111111111111111','info');
end try

begin catch
--错误了就回滚
rollback;
insert into tb_error_message
values('tba_delete','trigger',error_message(),getdate())

end catch

END
go

insert into tba
values('11','11')
/*
消息 3930,级别 16,状态 1,过程 tba_delete,第 16 行
当前事务无法提交,而且无法支持写入日志文件的操作。请回滚该事务。
语句已终止。
*/


--查询出错的信息
select *
from tb_error_message
/*
obj_name obj_type err_msg err_date
tba_delete trigger 将截断字符串或二进制数据。 2013-11-08 12:32:16.750
*/

3、忽略触发器中的错误

http://bbs.csdn.net/topics/390623172

我的情况是这样的: 当向A表插入数据时,需要向某用户发送信息通知,由于插入数据方是第三方软件做的,我控件不了,所以我在A表加个触发器来实现,现在问题是发送信息这里的错误是可以忽略的,就是说假如发送失败不影响数据的插入,请问我在触发器里如何忽略错误,让事务继续执行.

我的思路是:

--先保存一个事务点
SAVEPOINT xxx 

begin try  
   --或者是调用存储过程,或者就直接写代码
   exec 发送信息的存储过程      
end try  
begin catch  
  select '执行失败'  
  --一旦出错,只是回滚到上面的事务点,继续执行
  ROLLBACK [WORK] TO SAVEPOINT xxx
end catch  

--其他代码

4、如何实现一张表的某个字段为另一张表的某字段的计算值?

有一张表AA 有字段 a b c
另一张表BB 字段 aa bb cc

如何实现AA表的字段b(为整数型)中的数据 是BB表中的cc字段(为整数型)所有数据记录的总和(sum)!
要求做到BB表数据记录增加的同时AA表中字段b的记录值自动同步更新。

我的建议:

对的,触发器能实现你的需求,另外,索引能加快速度。

因为这种同步的需求,首先,不是跨越服务器的,一般跨越服务器的,可以考虑用数据库同步(异步)、镜像(同步)、日志传送(异步)等技术。

而你现在是在同一台服务器上,要同步2个表的数据,所以用触发器是最合适的,而且触发器本身的特性是,在同一个事务中,只要你的数据插入成功,那么另一个表的数据,也就是同步成功了,同样的,如果插入失败,报错,那么另一个表的数据也不会同步成功。

再有,就是效率的问题,如果每次都sum求和,而你的表里是上亿的数据,这个sum肯定是非常耗时的,所以这里采用的是,你插入1条数据,那么只要把这个插入的数据,通过update 表 set c = c + 插入的值
那么自然就加快了速度,而且不会出现并发的问题。

如果还想进一步加快速度,那么可以考虑,给A表建立索引,因为a表肯定记录也不会少,而且肯定是通过where条件来定义某一条记录,然后update ,这个时候通过索引,就能更快的找到这条记录,然后update。