在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_t2和dbo.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
 
0

SQL Server 2005 中实现通用的异步触发器架构的更多相关文章

  1. SQL Server 2005 中实现通用的异步触发器架构 (转)

    在SQL Server 2005中,通过新增的Service Broker可以实现异步触发器的处理功能.本文提供一种使用Service Broker实现的通用异步触发器方法. 在本方法中,通过Serv ...

  2. 浅析SQL Server 2005中的主动式通知机制

    一.引言 在开发多人同时访问的Web应用程序(其实不只这类程序)时,开发人员往往会在缓存策略的设计上狠下功夫.这是因为,如果将这种环境下不常变更的数据临时存放在应用程序服务器或是用户机器上的话,可以避 ...

  3. SQL Server 2005中的分区表

    记录笔记: 转自 猪八戒学做网站 SQL Server 2005中的分区表(一):什么是分区表?为什么要用分区表?如何创建分区表? SQL Server 2005中的分区表(二):如何添加.查询.修改 ...

  4. SQL Server 2005中更改sa的用户名和密码

    修改数据库SA账号名称的代码如下:  代码如下: Alter LOGIN sa DISABLE Alter LOGIN sa WITH NAME = [systemAccount] "sys ...

  5. SQL SERVER 2005中同义词实例

    From : http://www.cnblogs.com/jackyrong/archive/2006/11/15/561287.html 在SQL SERVER 2005中,终于出现了同义词了,大 ...

  6. SQL Server 2005中的分区表(六):将已分区表转换成普通表(转)

    我的俄罗斯名叫作“不折腾不舒服斯基”,所以,不将分区表好好折腾一下,我就是不舒服. 在前面,我们介绍过怎么样直接创建一个分区表,也介绍过怎么将一个普通表转换成一个分区表.那么,这两种方式创建的表有什么 ...

  7. SQL Server 2005中的CHECKSUM功能

    原文:SQL Server 2005中的CHECKSUM功能 转自此处 页面 checksum 是SQL2005的新功能,提供了一种比残缺页检测强大的机制检测IO方面的损坏.以下是详细描述: 页面 C ...

  8. SQL SERVER 2005中如何获取日期(一个月的最后一日、上个月第一天、最后一天、一年的第一日等等)

    原文:[转]SQL SERVER 2005中如何获取日期(一个月的最后一日.上个月第一天.最后一天.一年的第一日等等) 在网上找到的一篇文章,相当不错哦O(∩_∩)O~ //C#本周第一天       ...

  9. SQL Server 2005中设置Reporting Services发布web报表的匿名访问

    原文:SQL Server 2005中设置Reporting Services发布web报表的匿名访问 一位朋友提出个问题:集成到SQL Server 2005中的Reporting Services ...

随机推荐

  1. NoClassDefFoundError: org/slf4j/LoggerFactory解决

    缺少slf4j-api-1.5.2,slf4j-log4j12-1.5.2,log4j-1.2.8这几个包,导入即可

  2. 操作sqlserver数据库常用的三个方法

    1. ADO.NET -> 连接字符串,常用的两种方式: server=计算机名或ip\实例名;database=数据库名;uid=sa;pwd=密码; server=计算机名或ip\实例名;d ...

  3. C++中map的概念,与简单操作

     来源:http://blog.csdn.net/wallwind/article/details/6876892 C++map学习   map<Key, Data, Compare, Allo ...

  4. Todd's Matlab讲义第1讲:向量,函数和作图

    向量 Matlab 中最基本的对象是矩阵,向量是特殊的矩阵.行向量是\(1\times n\)矩阵,列向量是\(m\times 1\)矩阵.输入如下行向量: >> v=[0 1 2 3] ...

  5. [codeforces 325]B. Stadium and Games

    [codeforces 325]B. Stadium and Games 试题描述 Daniel is organizing a football tournament. He has come up ...

  6. Ubuntu上如何安装Java,Eclipse,Pydev,Python(自带,不用装),BeautifulSoup

    如何安装Java,如果出于编程的需要安装Java,需要安装的是JDK,而不仅仅是JRE,下面说说如何在Ubuntu下如何安装JDK:只有两步,1.下载并解压,2.配置环境变量1.下载并解压:下载地址: ...

  7. Java字符串split函数的注意事项

    Java字符串的split方法可以分割字符串,但和其他语言不太一样,split方法的参数不是单个字符,而是正则表达式,如果输入了竖线(|)这样的字符作为分割字符串,会出现意想不到的结果, 如, Str ...

  8. Ubuntu 更改文件夹权限及chmod详细用法

    转自: http://blog.chinaunix.net/uid-21880738-id-1813031.html 对Document/目录下的所有子文件与子目录执行相同的权限变更: Documen ...

  9. java并发库 Lock 公平锁和非公平锁

    jdk1.5并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,关于两者区别,java并发编程实践里面有解释 公平锁:   Threads acquir ...

  10. Java for LeetCode 077 Combinations

    Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. For exampl ...