我一直在处理与this question中提出的问题类似的问题 - 除了在我的上下文中我从ASP.NET网页执行命令。
Remus Rusanu在他的个人网站上发布了excellent solution using Service Broker Activation,我能够很好地工作。
但是我没有运气得到他接受参数运作的例子。它应该将数据插入withParam
表。
exec usp_AsyncExecInvoke @procedureName = N'usp_withParam'
, @p1 = 1.0, @n1 = N'@id'
, @p2 = N'Foo', @n2='@name'
, @p3 = 0xBAADF00D, @n3 = '@bytes'
, @token = @token output;
waitfor delay '00:00:05';
select * from AsyncExecResults;
select * from withParam;
相反,它完成没有错误,SELECT * withParam
显示一个空表。
SELECT * AsyncExecResults
表显示命令已提交但未显示任何错误信息(如果出现错误,则应如此)。
Passing Parameters to a Background Procedure
在网页的评论中已经建议使用quotename
时出现问题,如果您使用模式,最终会将其包裹起来[MySchema.SomeFunction]
删除quotename
并没有什么不同。
select @stmt = @stmtDeclarations + @stmtValues + N'
exec ' + @x.value(N'(//procedure/name)[1]', N'sysname');
由于查询正在执行而没有错误,因此我很少继续进行调试。有谁知道还有什么可能导致这种影响?
守则
create table [AsyncExecResults] (
[token] uniqueidentifier primary key
, [submit_time] datetime not null
, [start_time] datetime null
, [finish_time] datetime null
, [error_number] int null
, [error_message] nvarchar(2048) null);
go
create queue [AsyncExecQueue];
go
create service [AsyncExecService] on queue [AsyncExecQueue] ([DEFAULT]);
GO
-- Dynamic SQL helper procedure
-- Extracts the parameters from the message body
-- Creates the invocation Transact-SQL batch
-- Invokes the dynmic SQL batch
create procedure [usp_procedureInvokeHelper] (@x xml)
as
begin
set nocount on;
declare @stmt nvarchar(max)
, @stmtDeclarations nvarchar(max)
, @stmtValues nvarchar(max)
, @i int
, @countParams int
, @namedParams nvarchar(max)
, @paramName sysname
, @paramType sysname
, @paramPrecision int
, @paramScale int
, @paramLength int
, @paramTypeFull nvarchar(300)
, @comma nchar(1)
select @i = 0
, @stmtDeclarations = N''
, @stmtValues = N''
, @namedParams = N''
, @comma = N''
declare crsParam cursor forward_only static read_only for
select x.value(N'@Name', N'sysname')
, x.value(N'@BaseType', N'sysname')
, x.value(N'@Precision', N'int')
, x.value(N'@Scale', N'int')
, x.value(N'@MaxLength', N'int')
from @x.nodes(N'//procedure/parameters/parameter') t(x);
open crsParam;
fetch next from crsParam into @paramName
, @paramType
, @paramPrecision
, @paramScale
, @paramLength;
while (@@fetch_status = 0)
begin
select @i = @i + 1;
select @paramTypeFull = @paramType +
case
when @paramType in (N'varchar'
, N'nvarchar'
, N'varbinary'
, N'char'
, N'nchar'
, N'binary') then
N'(' + cast(@paramLength as nvarchar(5)) + N')'
when @paramType in (N'numeric') then
N'(' + cast(@paramPrecision as nvarchar(10)) + N',' +
cast(@paramScale as nvarchar(10))+ N')'
else N''
end;
-- Some basic sanity check on the input XML
if (@paramName is NULL
or @paramType is NULL
or @paramTypeFull is NULL
or charindex(N'''', @paramName) > 0
or charindex(N'''', @paramTypeFull) > 0)
raiserror(N'Incorrect parameter attributes %i: %s:%s %i:%i:%i'
, 16, 10, @i, @paramName, @paramType
, @paramPrecision, @paramScale, @paramLength);
select @stmtDeclarations = @stmtDeclarations + N'
declare @pt' + cast(@i as varchar(3)) + N' ' + @paramTypeFull
, @stmtValues = @stmtValues + N'
select @pt' + cast(@i as varchar(3)) + N'=@x.value(
N''(//procedure/parameters/parameter)[' + cast(@i as varchar(3))
+ N']'', N''' + @paramTypeFull + ''');'
, @namedParams = @namedParams + @comma + @paramName
+ N'=@pt' + cast(@i as varchar(3));
select @comma = N',';
fetch next from crsParam into @paramName
, @paramType
, @paramPrecision
, @paramScale
, @paramLength;
end
close crsParam;
deallocate crsParam;
select @stmt = @stmtDeclarations + @stmtValues + N'
exec ' + quotename(@x.value(N'(//procedure/name)[1]', N'sysname'));
if (@namedParams != N'')
select @stmt = @stmt + N' ' + @namedParams;
exec sp_executesql @stmt, N'@x xml', @x;
end
go
create procedure usp_AsyncExecActivated
as
begin
set nocount on;
declare @h uniqueidentifier
, @messageTypeName sysname
, @messageBody varbinary(max)
, @xmlBody xml
, @startTime datetime
, @finishTime datetime
, @execErrorNumber int
, @execErrorMessage nvarchar(2048)
, @xactState smallint
, @token uniqueidentifier;
begin transaction;
begin try;
receive top(1)
@h = [conversation_handle]
, @messageTypeName = [message_type_name]
, @messageBody = [message_body]
from [AsyncExecQueue];
if (@h is not null)
begin
if (@messageTypeName = N'DEFAULT')
begin
-- The DEFAULT message type is a procedure invocation.
--
select @xmlBody = CAST(@messageBody as xml);
save transaction usp_AsyncExec_procedure;
select @startTime = GETUTCDATE();
begin try
exec [usp_procedureInvokeHelper] @xmlBody;
end try
begin catch
-- This catch block tries to deal with failures of the procedure execution
-- If possible it rolls back to the savepoint created earlier, allowing
-- the activated procedure to continue. If the executed procedure
-- raises an error with severity 16 or higher, it will doom the transaction
-- and thus rollback the RECEIVE. Such case will be a poison message,
-- resulting in the queue disabling.
--
select @execErrorNumber = ERROR_NUMBER(),
@execErrorMessage = ERROR_MESSAGE(),
@xactState = XACT_STATE();
if (@xactState = -1)
begin
rollback;
raiserror(N'Unrecoverable error in procedure: %i: %s', 16, 10,
@execErrorNumber, @execErrorMessage);
end
else if (@xactState = 1)
begin
rollback transaction usp_AsyncExec_procedure;
end
end catch
select @finishTime = GETUTCDATE();
select @token = [conversation_id]
from sys.conversation_endpoints
where [conversation_handle] = @h;
if (@token is null)
begin
raiserror(N'Internal consistency error: conversation not found', 16, 20);
end
update [AsyncExecResults] set
[start_time] = @starttime
, [finish_time] = @finishTime
, [error_number] = @execErrorNumber
, [error_message] = @execErrorMessage
where [token] = @token;
if (0 = @@ROWCOUNT)
begin
raiserror(N'Internal consistency error: token not found', 16, 30);
end
end conversation @h;
end
else if (@messageTypeName = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
begin
end conversation @h;
end
else if (@messageTypeName = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
begin
declare @errorNumber int
, @errorMessage nvarchar(4000);
select @xmlBody = CAST(@messageBody as xml);
with xmlnamespaces (DEFAULT N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
select @errorNumber = @xmlBody.value ('(/Error/Code)[1]', 'INT'),
@errorMessage = @xmlBody.value ('(/Error/Description)[1]', 'NVARCHAR(4000)');
-- Update the request with the received error
select @token = [conversation_id]
from sys.conversation_endpoints
where [conversation_handle] = @h;
update [AsyncExecResults] set
[error_number] = @errorNumber
, [error_message] = @errorMessage
where [token] = @token;
end conversation @h;
end
else
begin
raiserror(N'Received unexpected message type: %s', 16, 50, @messageTypeName);
end
end
commit;
end try
begin catch
declare @error int
, @message nvarchar(2048);
select @error = ERROR_NUMBER()
, @message = ERROR_MESSAGE()
, @xactState = XACT_STATE();
if (@xactState <> 0)
begin
rollback;
end;
raiserror(N'Error: %i, %s', 1, 60, @error, @message) with log;
end catch
end
go
alter queue [AsyncExecQueue]
with activation (
procedure_name = [usp_AsyncExecActivated]
, max_queue_readers = 1
, execute as owner
, status = on);
go
-- Helper function to create the XML element
-- for a passed in parameter
create function [dbo].[fn_DescribeSqlVariant] (
@p sql_variant
, @n sysname)
returns xml
with schemabinding
as
begin
return (
select @n as [@Name]
, sql_variant_property(@p, 'BaseType') as [@BaseType]
, sql_variant_property(@p, 'Precision') as [@Precision]
, sql_variant_property(@p, 'Scale') as [@Scale]
, sql_variant_property(@p, 'MaxLength') as [@MaxLength]
, @p
for xml path('parameter'), type)
end
GO
-- Invocation wrapper. Accepts arbitrary
-- named parameetrs to be passed to the
-- background procedure
create procedure [usp_AsyncExecInvoke]
@procedureName sysname
, @p1 sql_variant = NULL, @n1 sysname = NULL
, @p2 sql_variant = NULL, @n2 sysname = NULL
, @p3 sql_variant = NULL, @n3 sysname = NULL
, @p4 sql_variant = NULL, @n4 sysname = NULL
, @p5 sql_variant = NULL, @n5 sysname = NULL
, @token uniqueidentifier output
as
begin
declare @h uniqueidentifier
, @xmlBody xml
, @trancount int;
set nocount on;
set @trancount = @@trancount;
if @trancount = 0
begin transaction
else
save transaction usp_AsyncExecInvoke;
begin try
begin dialog conversation @h
from service [AsyncExecService]
to service N'AsyncExecService', 'current database'
with encryption = off;
select @token = [conversation_id]
from sys.conversation_endpoints
where [conversation_handle] = @h;
select @xmlBody = (
select @procedureName as [name]
, (select * from (
select [dbo].[fn_DescribeSqlVariant] (@p1, @n1) AS [*]
WHERE @p1 IS NOT NULL
union all select [dbo].[fn_DescribeSqlVariant] (@p2, @n2) AS [*]
WHERE @p2 IS NOT NULL
union all select [dbo].[fn_DescribeSqlVariant] (@p3, @n3) AS [*]
WHERE @p3 IS NOT NULL
union all select [dbo].[fn_DescribeSqlVariant] (@p4, @n4) AS [*]
WHERE @p4 IS NOT NULL
union all select [dbo].[fn_DescribeSqlVariant] (@p5, @n5) AS [*]
WHERE @p5 IS NOT NULL
) as p for xml path(''), type
) as [parameters]
for xml path('procedure'), type);
send on conversation @h (@xmlBody);
insert into [AsyncExecResults]
([token], [submit_time])
values
(@token, getutcdate());
if @trancount = 0
commit;
end try
begin catch
declare @error int
, @message nvarchar(2048)
, @xactState smallint;
select @error = ERROR_NUMBER()
, @message = ERROR_MESSAGE()
, @xactState = XACT_STATE();
if @xactState = -1
rollback;
if @xactState = 1 and @trancount = 0
rollback
if @xactState = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror(N'Error: %i, %s', 16, 1, @error, @message);
end catch
end
go
-- Sample invocation example
-- The usp_withParam will insert
-- all the received parameters into this table
--
create table [withParam] (
id numeric(4,1) NULL
, name varchar(150) NULL
, date datetime NULL
, value int NULL
, bytes varbinary(max) NULL);
go
create procedure usp_withParam
@id numeric(4,1)
, @name varchar(150)
, @date datetime = NULL
, @value int = 0
, @bytes varbinary(max) = NULL
as
begin
insert into [withParam] (
id
, name
, date
, value
, bytes)
select @id as [id]
, @name as [name]
, @date as [date]
, @value as [value]
, @bytes as [bytes]
end
go
declare @token uniqueidentifier;
exec usp_AsyncExecInvoke @procedureName = N'usp_withParam'
, @p1 = 1.0, @n1 = N'@id'
, @p2 = N'Foo', @n2='@name'
, @p3 = 0xBAADF00D, @n3 = '@bytes'
, @token = @token output;
waitfor delay '00:00:05';
select * from AsyncExecResults;
select * from withParam;
go
答案 0 :(得分:2)
Remus的博客文章似乎忽略了我们需要使用SET ENABLE_BROKER来实际执行异步任务。
这对我有用(只有在你关闭数据库的所有连接都没问题时才运行此命令,此命令来自SET ENABLE_BROKER never completes in SQL Server):
ALTER DATABASE [Database_name] SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;
运行上面的命令后我运行了这个并很高兴看到“start_time”,“finish_time”,如果有错误,现在填充了“error_message”列!
SELECT * FROM [dbo].[AsyncExecResults]
编辑:另请参阅此文件以获取完整代码(作者从他的博客文章中发布的内容),当我使用上面的“SET ENABLE_BROKER”命令时,该代码对我有用:https://github.com/rusanu/async_tsql/blob/master/src/with_params.sql