zjcxc(邹建)的Blog - SQL Server

引用或者转载本BLOG的文章请注明原作者和出处,并保留原文章中的版权信息。谢谢!

用户操作
[即时聊天] [发私信] [加为好友]
邹建ID:zjcxc
485072次访问,排名96好友185人,关注者0
没有什么好介绍的
zjcxc的文章
原创 151 篇
翻译 0 篇
转载 9 篇
评论 522 篇
邹建的公告
嗨,您好!欢迎到zjcxc(邹建)的Blog

引用或者转载本BLOG的文章请注明原作者和出处,并保留原文章中的版权信息

《深入浅出——SQL Server 2005开发、管理与应用实例》 正式发行

中国互动出版社 当当网 销售商列表

最近评论
bruli:很好的教材,用了3天时间做了一遍

遇到一些问题,最后都解决了
第一次安装 MSDTC在node1上,此服务无法启动,卸载重装,依然不行。我干脆把那个node1节点停掉,在另一节点上重新安装MSDTC,成功。然后重新制作node1,再把它加入cluster,运行正常。

在node3安装SQL01,SQL02成功,但在将SQL01的管理权交给……
josy:邹老大,多个表导入一个excel文件的多个sheet里,可以吗?
giftzheng:4000条数据不适合树型

反正不管怎么样都要遍历
速度就慢了
giftzheng:4000条数据不适合树型

反正不管怎么样都要遍历
速度就慢了
Teng_s2000:XP好像可以下载一个SDK就行
文章分类
收藏
相册
我的女儿
我写的书
《中文版SQL Server 2000开发与管理应用实例》源代码下载
《深入浅出——SQL Server 2005开发、管理与应用实例》 - 当当网
《深入浅出——SQL Server 2005开发、管理与应用实例》- 中国互动出版社
学习资源
T-SQL脚本中心
教程在线
自建网站
先知电脑有限公司
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 SQL Server 2005 中实现通用的异步触发器架构收藏

新一篇: 《深入浅出——SQL Server 2005开发、管理与应用实例》简介 | 旧一篇: 发布一个"文本/文件朗读"小软件

  
在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]'