posts - 495,comments - 227,trackbacks - 0
          <2008年3月>
          2425262728291
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          常用鏈接

          留言簿(46)

          隨筆分類(476)

          隨筆檔案(495)

          最新隨筆

          搜索

          •  

          積分與排名

          • 積分 - 1396794
          • 排名 - 16

          最新評論

          閱讀排行榜

          評論排行榜

          在SQL Server 2005中,通過新增的Service Broker可以實現異步觸發器的處理功能。本文提供一種使用Service Broker實現的通用異步觸發器方法。
          在本方法中,通過Service Broker構造異步觸發器處理架構,對于要使用這種架構的表,只需要創建相應的觸發器及處理觸發器中數據的存儲過程,并且在異步觸發器架構中登記觸發器和處理的存儲過程即可。如果一個觸發器中的數據要被多個表使用,只需要在dbo.tb_async_trigger_subscribtion中登記相應處理數據的存儲過程即可,即一個表的數據變更可以被多個表訂閱(使用)。
          架構的步驟如下:
          1. 數據庫配置
          需要配置數據庫以允許使用Service Broker。本文以tempdb庫為例,故配置均在tempdb上下文中進行。
          USE tempdb
          GO
           
          -- 允許Service Broker
          ALTER DATABASE tempdb SET
          ENABLE_BROKER
          GO
           
          2. 構建異步觸發器相關的對象
          下面的T-SQL創建異步觸發器處理架構相關的對象。
          -- =======================================
          -- 異步觸發器對象
          -- 1. service broker 對象
          -- =======================================
          -- a. message type, 要求使用xml 傳遞數據
          CREATE MESSAGE TYPE MSGT_async_trigger
          VALIDATION = WELL_FORMED_XML
          GO
           
          -- b. 只需要發送消息
          CREATE CONTRACT CNT_async_trigger(
              MSGT_async_trigger SENT BY INITIATOR)
          GO
           
          -- c. 存儲消息的隊列
          CREATE QUEUE dbo.Q_async_trigger
          GO
           
          -- d. 用于消息處理的服務
          CREATE SERVICE SRV_async_trigger
              ON QUEUE dbo.Q_async_trigger(
                  CNT_async_trigger)
          GO
           
           
          -- =======================================
          -- 異步觸發器對象
          -- 2. 異步觸發器處理的對象
          -- =======================================
          -- a. 登記異步觸發器的表
          CREATE TABLE dbo.tb_async_trigger(
              ID int IDENTITY
                  PRIMARY KEY,
              table_name sysname,
              trigger_name sysname
          )
           
          -- b. 登記訂閱異步觸發器的存儲過程
          CREATE TABLE dbo.tb_async_trigger_subscriber(
              ID int IDENTITY
                  PRIMARY KEY,
              procedure_name sysname
          )
           
          -- c. 異步觸發器和存儲過程之間的訂閱關系
          CREATE TABLE dbo.tb_async_trigger_subscribtion(
              trigger_id int
                  REFERENCES dbo.tb_async_trigger(
                      ID),
              procedure_id int
                  REFERENCES dbo.tb_async_trigger_subscriber(
                      ID),
              PRIMARY KEY(
                  trigger_id, procedure_id)
          )
          GO
           
          -- d. 發送消息的存儲過程
          CREATE PROC dbo.p_async_trigger_send
              @message xml
          AS
          SET NOCOUNT ON
          DECLARE
              @handle uniqueidentifier
          BEGIN DIALOG CONVERSATION @handle
              FROM SERVICE [SRV_async_trigger]
              TO SERVICE N'SRV_async_trigger'
              ON CONTRACT CNT_async_trigger
              WITH
                  ENCRYPTION = OFF;
          SEND
              ON CONVERSATION @handle
              MESSAGE TYPE MSGT_async_trigger(
                  @message);
          -- 消息發出即可, 不需要回復, 因此發出后即可結束會話
          END CONVERSATION @handle
          GO
           
          -- e. 處理異步觸發器發送的消息
          CREATE PROC dbo.p_async_trigger_process
          AS
          SET NOCOUNT ON
          DECLARE
              @handle uniqueidentifier,
              @message xml,
              @rows int
          SET @rows = 1
          WHILE @rows > 0
          BEGIN
              -- 處理已經收到的消息
              WAITFOR(
                  RECEIVE TOP(1)
                      @handle = conversation_handle,
                      @message = CASE
                                      WHEN message_type_name = N'MSGT_async_trigger'
                                          THEN CONVERT(xml, message_body)
                                      ELSE NULL
                                  END
                  FROM dbo.Q_async_trigger
              ), TIMEOUT 10
              SET @rows = @@ROWCOUNT
              IF @rows > 0
              BEGIN
                  -- 結束會話
                  END CONVERSATION @handle;
           
                  -- 處理消息
                  -- a. 取發送者信息
                  DECLARE
                      @table_name sysname,
                      @trigger_name sysname,
                      @sql nvarchar(max)
                  SELECT
                      @table_name = @message.value('(/root/table_name)[1]', 'sysname'),
                      @trigger_name = @message.value('(/root/trigger_name)[1]', 'sysname')
           
                  -- b. 調用異步觸發器訂閱的存儲過程
                  ;WITH
                  SUB AS(
                      SELECT
                          TR.table_name,
                          TR.trigger_name,
                          SUB.procedure_name
                      FROM dbo.tb_async_trigger TR,
                          dbo.tb_async_trigger_subscriber SUB,
                          dbo.tb_async_trigger_subscribtion TRSUB
                      WHERE TRSUB.trigger_id = TR.ID
                          AND TRSUB.procedure_id = SUB.ID
                  )
                  SELECT
                      @sql = (
                              SELECT
                                  N'
          EXEC ' + procedure_name + N'
              @message
          '
                              FROM SUB
                              WHERE table_name = @table_name
                                  AND trigger_name = @trigger_name
                              FOR XML PATH(''), ROOT('r'), TYPE
                          ).value('(/r)[1]', 'nvarchar(max)')
                  EXEC sp_executesql @sql, N'@message xml', @message
              END
          END
          GO
           
          -- f. 綁定處理的存儲過程到隊列
          ALTER QUEUE dbo.Q_async_trigger
              WITH ACTIVATION(
                  STATUS = ON,
                  PROCEDURE_NAME = dbo.p_async_trigger_process,
                  MAX_QUEUE_READERS = 10,
                  EXECUTE AS OWNER)
          GO
           
          3. 使用示例
          下面的T-SQL演示使用異步觸發器構架。示例中創建了三個表:
          Dbo.t1               這個是源表,此表的數據變化將用于其他表
          Dbo.t2               這個表要求保持與dbo.t1同步
          Dbo.tb_log       這個表記錄dbo.t1中的數據變化情況
          觸發器 TR_async_trigger 用于將表Dbo.t1中的數據變化發送到異步觸發器構架中。dbo.p_Sync_t1_t2dbo.p_Record_log用于處理dbo.t1于中變化的數據。
          在處理時,需要把相關的信息登記到異步觸發器架構的表中。
          -- =======================================
          -- 3. 使用示例
          -- =======================================
          -- ===============================
          -- 測試對象
          -- a. 源表
          CREATE TABLE dbo.t1(
              id int IDENTITY
                  PRIMARY KEY,
              col int
          )
          -- b. 同步的目的表
          CREATE TABLE dbo.t2(
              id int IDENTITY
                  PRIMARY KEY,
              col int
          )
          -- c. 記錄操作的日志表
          CREATE TABLE dbo.tb_log(
              id int IDENTITY
                  PRIMARY KEY,
              user_name sysname,
              operate_type varchar(10),
              inserted xml,
              deleted xml
          )
          GO
           
          -- a. 異步發送處理消息的觸發器
          CREATE TRIGGER TR_async_trigger
          ON dbo.t1
          FOR INSERT, UPDATE, DELETE
          AS
          IF @@ROWCOUNT = 0
              RETURN
           
          SET NOCOUNT ON
           
          -- 將要發送的數據生成xml 數據
          DECLARE
              @message xml
          SELECT
              @message = (
                      SELECT
                          table_name = (
                                  SELECT TOP 1
                                      OBJECT_NAME(parent_object_id)
                                  FROM sys.objects
                                  WHERE object_id = @@PROCID),
                          trigger_name = OBJECT_NAME(@@PROCID),
                          user_name = SUSER_SNAME(),
                          inserted = (
                                  SELECT * FROM inserted FOR XML AUTO, TYPE),
                          deleted = (
                                  SELECT * FROM deleted FOR XML AUTO, TYPE)
                      FOR XML PATH(''), ROOT('root'), TYPE
                  )
          -- 發送消息
          EXEC dbo.p_async_trigger_send
              @message = @message
          GO
           
          -- b. 處理異步觸發器的存儲過程
          -- b.1 同步到t2 的存儲過程
          CREATE PROC dbo.p_Sync_t1_t2
              @message xml
          AS
          SET NOCOUNT ON
          DECLARE
              @inserted bit,
              @deleted bit
          SELECT
              @inserted = @message.exist('/root/inserted'),
              @deleted = @message.exist('/root/deleted')
          IF @inserted = 1
              IF @deleted = 1 -- 更新
              BEGIN
                  ;WITH
                  I AS(
                      SELECT
                          id = T.c.value('@id[1]', 'int'),
                          col = T.c.value('@col[1]', 'int')
                      FROM @message.nodes('/root/inserted/inserted') T(c)
                  ),
                  D AS(
                      SELECT
                          id = T.c.value('@id[1]', 'int'),
                          col = T.c.value('@col[1]', 'int')
                      FROM @message.nodes('/root/deleted/deleted') T(c)
                  )
                  UPDATE A SET
                      col = I.col
                  FROM dbo.t2 A, I, D
                  WHERE A.ID = I.ID
                      AND I.ID = D.ID
              END
              ELSE            -- 插入
              BEGIN
                  SET IDENTITY_INSERT dbo.t2 ON
                  ;WITH
                  I AS(
                      SELECT
                          id = T.c.value('@id[1]', 'int'),
                          col = T.c.value('@col[1]', 'int')
                      FROM @message.nodes('/root/inserted/inserted') T(c)
                  )
                  INSERT dbo.t2(
                      id, col)
                  SELECT
                      id, col
                  FROM I
                  SET IDENTITY_INSERT dbo.t2 OFF
              END
          ELSE                -- 刪除
          BEGIN
              ;WITH
              D AS(
                  SELECT
                      id = T.c.value('@id[1]', 'int'),
                      col = T.c.value('@col[1]', 'int')
                  FROM @message.nodes('/root/deleted/deleted') T(c)
              )
              DELETE A
              FROM dbo.t2 A, D
              WHERE A.ID = D.ID
          END
          GO
           
          -- b.2 記錄操作記錄到dbo.tb_log 的存儲過程
          CREATE PROC dbo.p_Record_log
              @message xml
          AS
          SET NOCOUNT ON
          DECLARE
              @inserted bit,
              @deleted bit
          SELECT
              @inserted = @message.exist('/root/inserted'),
              @deleted = @message.exist('/root/deleted')
          INSERT dbo.tb_log(
              user_name,
              operate_type,
              inserted,
              deleted)
          SELECT
              @message.value('(/root/user_name)[1]', 'sysname'),
              operate_type = CASE
                              WHEN @inserted = 1 AND @deleted = 1 THEN 'update'
                              WHEN @inserted = 1 THEN 'insert'
                              WHEN @deleted = 1 THEN 'delete'
                          END,
              @message.query('/root/inserted'),
              @message.query('/root/deleted')
          GO
           
           
          -- ===============================
          -- 在異步觸發器處理系統中登記對象
          INSERT dbo.tb_async_trigger(
              table_name, trigger_name)
          VALUES(
              N't1', N'TR_async_trigger')
           
          INSERT dbo.tb_async_trigger_subscriber(
              procedure_name)
          SELECT N'dbo.p_Sync_t1_t2' UNION ALL
          SELECT N'dbo.p_Record_log'
           
          INSERT dbo.tb_async_trigger_subscribtion(
              trigger_id, procedure_id)
          SELECT 1, 1 UNION ALL
          SELECT 1, 2
          GO
           
          4. 使用測試
          下面的T-SQL修改表dbo.t1中的數據,并檢查dbo.t2、dbo.tb_log中的數據,以確定異步觸發器架構的工作是否成功。
          執行完成后可以看到dbo.t2、dbo.tb_log中有相關的記錄。
          -- ===============================
          -- 測試
          INSERT dbo.t1
          SELECT 1 UNION ALL
          SELECT 2
           
          UPDATE dbo.t1 SET
              col = 2
          WHERE id = 1
           
          DELETE dbo.t1
          WHERE id = 2
           
          -- 顯示結果
          WAITFOR DELAY '00:00:05' -- 延遲5 分鐘, 以便有時間處理消息(因為是異步的)
          SELECT * FROM dbo.t2
          SELECT * FROM dbo.tb_log
          GO
           
          5. 使用測試
          下面的T-SQL刪除本文中建立的所有對象。
          -- =======================================
          -- 5. 刪除相關的對象
          -- =======================================
          -- a. 刪除service broker 對象
          DROP SERVICE SRV_async_trigger
          DROP QUEUE dbo.Q_async_trigger
          DROP CONTRACT CNT_async_trigger
          DROP MESSAGE TYPE MSGT_async_trigger
          GO
           
          -- b. 刪除異步觸發器處理的相關對象
          DROP PROC dbo.p_async_trigger_process
          DROP PROC dbo.p_async_trigger_send
          DROP TABLE dbo.tb_async_trigger_subscribtion
          DROP TABLE dbo.tb_async_trigger_subscriber
          DROP TABLE dbo.tb_async_trigger
          GO
           
          -- c. 刪除測試的對象
          DROP TABLE dbo.tb_log, dbo.t1, dbo.t2
          DROP PROC dbo.p_Sync_t1_t2, dbo.p_Record_log

          posted on 2008-03-11 09:38 SIMONE 閱讀(707) 評論(0)  編輯  收藏 所屬分類: SQL SERVER
          主站蜘蛛池模板: 黄陵县| 永修县| 从化市| 潢川县| 新田县| 盘山县| 永州市| 兴安盟| 郓城县| 隆尧县| 左云县| 株洲市| 泸定县| 疏勒县| 简阳市| 茶陵县| 揭西县| 开远市| 宜宾市| 刚察县| 商城县| 招远市| 财经| 南华县| 石首市| 阜平县| 雷波县| 阜阳市| 周至县| 恩平市| 宜兴市| 勐海县| 常州市| 宜川县| 安新县| 辽源市| 浦江县| 宜宾市| 达日县| 抚宁县| 瑞安市|