【发布时间】:2021-07-21 03:17:36
【问题描述】:
我需要能够“捕捉”SQL Server 会话的结束,以便我可以在会话使用的任何数据库上运行例程。我找到了一些队列通知示例,这就是我目前正在尝试使用的。
在下面的代码中,它按原样工作。但是,当我在“激活”存储过程中删除游标 Open 上的 cmets 时,@@ROWCOUNT 返回 0,然后队列被停用。进一步的消息只是继续建立。执行RECEIVE TOP(1) * FROM dbo.queNotification 会出错:
Msg 9617, Level 16, State 1, Procedure dbo.ReceiveEvent, Line 12 [Batch Start Line 177]
The service queue "queNotification" is currently disabled.
再次启用队列后,我尝试手动运行例程...它运行良好。它只是不想在代理设置下运行。
create table ##ENLog (
[msg] varchar(max),
[when] datetime2 NOT NULL CONSTRAINT DF_LogoutAudit__time_acquired DEFAULT sysdatetime()
)
CREATE DATABASE ENTesting;
GO
ALTER DATABASE ENTesting SET ENABLE_BROKER;
GO
USE ENTesting;
CREATE QUEUE queNotification;
CREATE SERVICE svcNotification ON QUEUE queNotification ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
CREATE EVENT NOTIFICATION enEvents ON SERVER FOR AUDIT_LOGOUT TO SERVICE 'svcNotification', 'current database';
GO
ALTER QUEUE dbo.queNotification WITH ACTIVATION (PROCEDURE_NAME = dbo.ReceiveEvent, STATUS = OFF);
ALTER QUEUE dbo.queNotification WITH STATUS = OFF;
GO
CREATE OR ALTER PROCEDURE dbo.ReceiveEvent AS
SET NOCOUNT ON
DECLARE @MsgBody XML
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION
insert into ##ENLog (msg) values ('START');
WAITFOR ( -- Receive the next available message from the queue
RECEIVE TOP(1) -- just handle one message at a time
@MsgBody = CAST(message_body AS XML)
FROM queNotification), TIMEOUT 1000 -- if the queue is empty for one second, relax
IF (@@ROWCOUNT = 0) -- If we didn't get anything, bail out
BEGIN
ROLLBACK TRANSACTION; -- this will undo the RECEIVE
insert into ##ENLog (msg) values ('NO MORE ROWS');
BREAK;
END ELSE BEGIN
BEGIN TRAN
insert into ##ENLog (msg) values ('GOT ROW');
declare @spid smallint = @MsgBody.value('(/EVENT_INSTANCE/SPID)[1]', 'smallint')
declare @dbid int = @MsgBody.value('(/EVENT_INSTANCE/DatabaseID)[1]', 'int')
declare @host_name sysname = @MsgBody.value('(/EVENT_INSTANCE/HostName)[1]', 'sysname')
declare @login_name sysname = @MsgBody.value('(/EVENT_INSTANCE/LoginName)[1]', 'sysname')
declare @msg nvarchar(max) = 'spid ' + convert(nvarchar, @spid) + char(10) + char(9) + 'dbid ' + convert(nvarchar, @dbid) + char(10) + char(9) + 'host ' + @host_name + char(10) + char(9) + 'login ' + @login_name + char(10) + char(9);
insert into ##ENLog (msg) values (@msg);
declare @cur cursor
declare @dbname sysname
begin
insert into ##ENLog (msg) values ('Start Cursor');
SET @cur = CURSOR FOR
select name from sys.databases d
where database_id>4 and object_id([name] + '.mds.DeinitSession') IS NOT NULL
BEGIN TRY
print '' -- nop
---- uncomment anything below here and it stops working
---- OPEN @cur
---- insert into ##ENLog (msg) values ('Cursor Opened');
---- FETCH NEXT FROM @cur INTO @dbname
---- insert into ##ENLog (msg) values ('Fetch Called');
---- while @@FETCH_STATUS = 0 begin
---- insert into ##ENLog (msg) values ('Calling Deinit on ' + @dbname);
----
---- declare @sql nvarchar(256) = 'USE btrtest' + char(10) + 'insert into ##ENLog (msg) values (''deinit ' + @dbname + ''')'
---- insert into ##ENLog (msg) values (@sql);
---- execute(@sql)
---- FETCH NEXT FROM @cur INTO @dbname
---- end
---- insert into ##ENLog (msg) values ('Close Cursor');
---- CLOSE @cur
---- DEALLOCATE @cur
END TRY BEGIN CATCH
set @msg = N'Exception Error: ' + N' Msg ' + convert(nvarchar, ERROR_NUMBER()) + N', Level ' + convert(nvarchar, ERROR_SEVERITY()) + N', Stat ' + convert(nvarchar, ERROR_STATE()) + N', Line ' + convert(nvarchar, ERROR_LINE()) + N', Proc ' + isnull(ERROR_PROCEDURE(),'--') + N' ' + ERROR_MESSAGE();
insert into ##ENLog (msg) values (@msg);
END CATCH
end
insert into ##ENLog (msg) values ('Commit');
COMMIT TRANSACTION
-- Commit the transaction. At any point before this, we could roll back - the received message would be back on the queue AND the response wouldn't be sent.
COMMIT TRANSACTION
END
END
GO
ALTER QUEUE dbo.queNotification
WITH ACTIVATION ( PROCEDURE_NAME = dbo.ReceiveEvent, STATUS = ON, MAX_QUEUE_READERS = 1, EXECUTE AS OWNER)
GO
ALTER QUEUE dbo.queNotification WITH STATUS = ON;
GO
SELECT * FROM dbo.queNotification;
select * from ##ENLog
【问题讨论】:
标签: sql-server event-handling message-queue database-cursor