半城烟沙

导航

关于项目中导入数据时,激活相应数据库表的触发器【7.12教训,当头一棒,以此为鉴,小区覆盖项目涉及的类似触发器也需要改变~】

  因为熬夜看世界杯决赛,今天上午就在宿舍睡过去了。中午胆战心惊地找到老叶撒了个谎,说昨晚下雨宿舍楼顶漏水才没来,老叶就嗯嗯了两声,也没说什么,松了一口气。其实说完这些,我出来就好了,可是嘴太快,顺便又提了下前天触发器的问题,结果被老叶一顿鄙视,说什么小c弄好了,这是我没有料到的,只好点头如捣蒜,赶紧着溜出来了。

 回到实验室,心里纳闷小c真的弄出来了,不可能吧。但是当小l把邮件转发给我,我看完之后,真的如当头挨一棒,人家小c真的弄出来了,还把自己的思路画成流程图,写了个文档像模像样地发给了老师。OMG,想当初,老叶也逼着我改触发器的流程,他想法丰富,跟我说了一堆减少临时表的思路,可是呢,不知道当时我怎么想的,愣是认为原来的思路不能改变,死命在其他地方找问题,虽然最后在其他地方找到了问题,确实把效率提高了,但是现在看来,人家小c真是有想法,很漂亮地改了触发器的流程,小c的具体改法如下:

以前数据库触发器设计的时候在时间和空间存储效率上存在一些冗余。以前触发器(以tbBSC为例)的流程是:

改进后触发器流程(以tbBSC为例)如下:

 

 

根据上面的流程,知道#inserted表可以删除,主属性以后再进行判断,#insertedTemp插入表可以从inserted表和#updateTemp更新表做差获取,减少与tbBSC比较扫描次数。获得#updateTemp更新表和#insertedTemp插入表SQL语句可以进行优化,提高查询效率。

鉴于此,触发器(以tbBSC为例)需要更改的地方有:

1.  将#inserted删除,数据都从触发器维护的表inserted获取,节省存储空间

2.  对SQL语句进行改进,原来筛选获得#updateTemp更新表的时候语句为:

select * into #updateBSCTemp from inserted

where exists(select * from tbBSC where inserted.MSCID = tbBSC.MSCID and inserted.BSCID = tbBSC.BSCID)

改为:select * into #updateBSCTemp from inserted

where exists(select MSCID,BSCID from tbBSC where inserted.MSCID = tbBSC.MSCID and inserted.BSCID = tbBSC.BSCID)

改进查询速率

3.  针对以前#insertedTemp插入表是根据inserted表和数据库表tbBSC比较获取,现在#insertedTemp插入表是根据inserted表和#updateBSCTemp更新表比较获取。改进了查询速度。同样,为了提高查询速率,将select * 改为from tbBSC select MSCID,BSCID from tbBSC,并且由于开始并没有把主属性为空的数据剔除,因为在where字句中要加上限定条件。语句如下:

select * into #insertBSCTemp from inserted

where MSCID is not null and BSCID is not null and not exists(select MSCID,BSCID from #updateBSCTemp where inserted.MSCID = #updateBSCTemp.MSCID and inserted.BSCID = #updateBSCTemp.BSCID)

具体代码请参见附件:BSCInsert.sql,其中更改之处有注释说明。

BSCInsert.sql如下:

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

-- =============================================
-- Author:  <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
ALTER TRIGGER [BSCInsert]
   ON  [dbo].[tbBSC]
   instead of insert
AS
BEGIN
 -- SET NOCOUNT ON added to prevent extra result sets from
 -- interfering with SELECT statements.
 SET NOCOUNT ON;

    -- Insert statements for trigger here
 begin  
     declare @i int -- 记录总共的列数,以便循环
     declare @tableCount int -- 记录插入表中的列数,以便循环
     declare @columnName varchar(60) --记录每列的列名
     declare @Count int
     DECLARE @str varchar(500)  --插入字符串

     select @i = count(*)from syscolumns where id = object_id('tbBSC')
     --删除#inserted 节省存储空间 modify 2010-07-10
           --select * into #inserted from inserted where inserted.MSCID is not null and inserted.BSCID is not null

   --设计插入表
   --modify 2010-07-10
     --将select * 改为select MSCID,BSCID提高查询速率
   select * into #updateBSCTemp
   from inserted
   where exists(select MSCID,BSCID from tbBSC where inserted.MSCID = tbBSC.MSCID and inserted.BSCID = tbBSC.BSCID)

   --判断数据库表中是否已经存在相同的记录  有的话更新  否则插入
   --判断数据库表中是否已经存在相同的记录
  
   set @Count = (select count(*) from #updateBSCTemp)
   --此处通过
print rtrim(@Count)
   if (@Count > 0) --更新
    while @i > 0
     begin
             -- 获得列名
      set @columnName = COL_NAME(OBJECT_ID('tbBSC'), @i)
      
      select @str='update tbBSC set '+ @columnName +'= #updateBSCTemp.'+ @columnName+ ' from tbBSC,#updateBSCTemp  where #updateBSCTemp.BSCID = tbBSC.BSCID and #updateBSCTemp.MSCID = tbBSC.MSCID and #updateBSCTemp.'+ @columnName +' is not null '
      
      EXEC (@str)

      if @columnName = 'Longitude'
       begin
        --修改tbBTS表Longitude字段
        update tbBSC
        set Longitude=Longitude/14400
        where Longitude is not null and Longitude>180
       end

      else if  @columnName = 'Latitude'
       begin
       --修改tbBTS表Latitude字段
        update tbBSC
        set Latitude=Latitude/14400
        where Latitude is not null and Latitude>90
       end
     set @i = @i - 1
    end --if更新

    --删除中建表
    --delete from #updateBSCTemp

    --设计插入表
    --modify 2010-07-10
          --将select * 改为select MSCID,BSCID提高查询速率
    --加上限制条件,主属性不能为空
    --#insertBSCTemp改为从#updateBSCTemp和inserted做差获取
    select * into #insertBSCTemp
    from inserted
    where MSCID is not null and BSCID is not null and not exists(select MSCID,BSCID from #updateBSCTemp where inserted.MSCID = #updateBSCTemp.MSCID and inserted.BSCID = #updateBSCTemp.BSCID)
    
    delete from #updateBSCTemp
    
    set @Count = (select count(*) from #insertBSCTemp)
    --此处通过
print rtrim(@Count)
    if (@Count > 0)  --插入
        begin
      
      insert into tbBSC select * from #insertBSCTemp 

        while @i > 0
      begin
              -- 获得列名
       set @columnName = COL_NAME(OBJECT_ID('tbBSC'), @i)
--       
       SELECT @str = 'insert into tbNEUpdateLog select ''user'',''ip'',''BSC'',I.BSCName, ''插入'',''' + @columnName + ''',NULL,I.' + @columnname + ',getdate() from #insertBSCTemp as I where I.'+ @columnname + ' is not NULL'
         
       EXEC (@str)

       if  @columnName = 'Longitude'
        begin
         --修改tbCell表Longitude字段
         update tbBSC
         set Longitude=Longitude/14400
         where Longitude is not null and Longitude>180
        end

       else if  @columnName = 'Latitude'
        begin
        --修改tbCell表Latitude字段
         update tbBSC
         set Latitude=Latitude/14400
         where Latitude is not null and Latitude>90
        end

       set @i = @i - 1
      end --else --插入
    end
   --删除中建表
   delete from #insertBSCTemp
 end
END

看完之后,我真的很怀疑自己的能力了,为什么我就没有想到呢?其实道理很简单吗?我为什么就不能做到触类旁通,举一反三呢?我太死板了,很多思想在很多学科里是互通的,为什么我就这么笨呢?就是一个很简单的想法嘛,但是没想到的话,就是不简单的,希望自己以后遇到问题能多思考,多尝试,不能满足于结果,还要看看过程~~~

另外,关于小c写的这个触发器,我觉得还可以循环更新和循环插入两处还可以改进,可以创建存储过程,重用执行计划,提高效率,具体改进是:(以插入相应列变量值为例)

CREATE proc [dbo].[proc_insert_tmp](@columnName varchar(255),@tablename varchar(255))
as

declare @sqlinsert nvarchar(MAX)
set @sqlinsert='insert into tbNEUpdateLog select ''user'',''ip'',''BSC'',I.BSCName, ''插入'',''' + @columnName + ''',NULL,I.' + @columnname + ',getdate() from '+@sqlinsert+' as I where I.'+ @columnname + ' is not NULL'

exec(@sqlinsert)

posted on 2010-07-12 20:19  半城烟沙  阅读(327)  评论(0编辑  收藏  举报