zoukankan      html  css  js  c++  java
  • SQL Server如何用触发器捕获DML操作的会话信息

    需求背景

      

        上周遇到了这样一个需求,维护人员发现一个表的数据经常被修改,由于历史原因;文档缺少;以及维护人员的经常变更,导致他们对系统也业务也不完全熟悉,他们也不完全清楚哪些系统和应用程序会对这个表的数据进行操作。现在他们想找出有哪些服务器,哪些应用程序会对这个表进行INSERT、UPDATE操作。那么问题来了,怎么去解决这个问题呢?

     

    解决方案

    由于数据库版本是标准版,我们选择了使用触发器来捕获进行DML操作的会话的相关信息,例如,Host_Name、Program_Name等 ,选择触发器是因为简单直接。我们先创建一个表名为TEST的表,假设我们想监控有哪些应用服务器,以及那些应用程序会对表TEST进行INSERT、UPDATE操作。

    USE [AdventureWorks2014]
    GO
     
     
    IF NOT EXISTS (SELECT 1 FROM sys.sysobjects WHERE id=object_id(N'[dbo].[TEST]') AND OBJECTPROPERTY(id, N'IsTable')=1 )
    BEGIN
    CREATE TABLE [dbo].[TEST](
        [OBJECT_ID] [INT] NOT NULL,
        [NAME] [VARCHAR](8) NULL,
        CONSTRAINT PK_TEST    PRIMARY KEY (OBJECT_ID)
    ) 
    END
    GO
     
    INSERT INTO dbo.TEST
    SELECT 1, 'kerry' UNION ALL
    SELECT 2, 'jimmy'

     

    那么我们接下来在表上面新增几个字段 [HOST_NAME]、[PROGRAM_NAME]、LOGIN_NAME用来记录最后一次修改该记录的会话信息,另外创建触发器TRG_TEST来更新这几个字段

    ALTER TABLE TEST ADD  [HOST_NAME]  NVARCHAR(256)
    ALTER TABLE TEST ADD  [PROGRAM_NAME] NVARCHAR(256);
    ALTER TABLE TEST ADD  LOGIN_NAME  NVARCHAR(256);
     
    CREATE TRIGGER TRG_TEST ON dbo.TEST AFTER 
    INSERT,UPDATE
    AS 
     
    IF (EXISTS(SELECT  1 FROM  INSERTED))
    BEGIN
     
        UPDATE  dbo.TEST
        SET     dbo.TEST.[HOST_NAME] = ( SELECT host_name
                                         FROM   sys.dm_exec_sessions
                                         WHERE  session_id = @@SPID
                                       ) ,
                dbo.TEST.PROGRAM_NAME = ( SELECT    program_name
                                          FROM      sys.dm_exec_sessions
                                          WHERE     session_id = @@SPID
                                        ) ,
                dbo.TEST.LOGIN_NAME = ( SELECT  login_name
                                        FROM    sys.dm_exec_sessions
                                        WHERE   session_id = @@SPID
                                      )
        FROM    dbo.TEST t
                INNER JOIN INSERTED i ON t.OBJECT_ID = i.OBJECT_ID
    END
    GO

     

    接下来,我们来简单测试一下,如下所示,分布插入、更新一条记录

    INSERT INTO dbo.TEST(OBJECT_ID,NAME)
    SELECT 3,'ken'
     
    UPDATE dbo.TEST SET NAME='Richard' WHERE  OBJECT_ID=2;

     

    如下所示,因为我只是用SSMS更新,插入数据,所以捕获的是Microsoft SQL Server Management Studio - Query。

    clipboard

     

    这这种方式还有一个弊端,那就是如果应用程序的SQL,写得不够健壮的话,那么增加字段就会导致以前的应用程序出现问题,例如,应用程序有下面这样的SQL,增加字段后,它就会报错。

    INSERT INTO dbo.TEST
    SELECT 3,'ken'

     

    所以这种方案不太可行,会增加应用程序出现Bug的风险。那么其实我们可以新建一个表,每当原表TEST有INSERT、UPDATE操作时,通过触发器捕获会话进程信息,然后插入该表(注意,新建的表包含源表的主键字段,例如这里TEST的主键字段为OBJECT_ID,那么我们下面就包含OBJECT_ID)

    USE [AdventureWorks2014]
    GO
     
    DROP TABLE  dbo.[TRG_TEST_SESSION_INFO];
    GO
     
    IF NOT EXISTS (SELECT 1 FROM sys.sysobjects WHERE id=object_id(N'[dbo].[TRG_TEST_SESSION_INFO]') AND OBJECTPROPERTY(id, N'IsTable')=1 )
    BEGIN
    CREATE TABLE [TRG_TEST_SESSION_INFO](
        [ID]               INT NOT NULL IDENTITY(1,1),
        [OBJECT_ID]        INT,
        [HOST_NAME]        NVARCHAR(256),
        [PROGRAM_NAME]     NVARCHAR(256),
        [LOGIN_NAME]       NVARCHAR(256),
        CONSTRAINT PK_TRG_TEST_SESSION_INFO    PRIMARY KEY (ID)
    ) 
    END
    GO
     
    CREATE TRIGGER TRG_TEST_SESSION ON dbo.TEST
    AFTER INSERT ,UPDATE
    AS
     
    IF (EXISTS(SELECT  1 FROM  INSERTED))
    BEGIN
     
        /*
        INSERT INTO dbo.[TRG_TEST_SESSION_INFO]
        SELECT  (SELECT I.OBJECT_ID FROM INSERTED I), HOST_NAME,program_name,login_name
                                         FROM   sys.dm_exec_sessions
                                         WHERE  session_id = @@SPID*/
        INSERT INTO dbo.[TRG_TEST_SESSION_INFO]
        SELECT  I.OBJECT_ID, S.HOST_NAME,S.PROGRAM_NAME,S.LOGIN_NAME
                                         FROM   sys.dm_exec_sessions s,
                                                Inserted i
                                         WHERE  session_id = @@SPID
     
      
    END
    GO

     

    在运行一小段时间后,如果已经找出了哪些服务器、哪些应用程序会对这些表操作后,那么就必须马上删除这些表和触发器,避免长时间运行,影响性能。

  • 相关阅读:
    tar命令,vi编辑器
    Linux命令、权限
    Color Transfer between Images code实现
    利用Eclipse使用Java OpenCV(Using OpenCV Java with Eclipse)
    Matrix Factorization SVD 矩阵分解
    ZOJ Problem Set
    Machine Learning
    ZOJ Problem Set
    ZOJ Problem Set
    ZOJ Problem Set
  • 原文地址:https://www.cnblogs.com/kerrycode/p/6589609.html
Copyright © 2011-2022 走看看