如何让 SQL Server 2019 假装存储过程正在进行中?

How can I make SQL Server 2019 pretend that a stored proc is in progress?

我有一个存储过程,可以从实时生产数据库中刷新报告数据库中的多个表。

我运行:

EXEC Reporting.dbo.RefreshTemps

这个存储过程非常简单,它所做的只是运行分类几个表,然后运行插入它们以获得刷新的数据快照。大约需要 10 分钟才能完成,因为它们是非常厚实的桌子。

但是,许多人、进程或其他程序可能希望在白天使用此程序。完全合理的是,用户 A 可能会启动进程 运行ning,然后用户 B 在用户 A 的 运行 完成之前启动相同的进程 运行ning。

这意味着一旦用户 A 的过程完成,用户 B 的过程将处于 t运行更新和刷新相同表的过程中。这会很糟糕。

理想情况下,SQL 不会 运行 用户 B 的 proc,等待用户 A 的 运行 完成,用户 A 和用户 B 将同时公开更新的表时间.

有什么方法可以让 SQL 误以为用户 B 的进程正常 运行,但它实际上在等待用户 A 的 运行 完成?

这里最简单的做法是使用 SQL 服务器代理作业。您可以使用 sp_start_job 开始一项工作,并且任何时候都只能 运行 一份工作副本。

就像 Dan Guzman 建议的那样,您也可以使用 sp_getapplock。但是您可能想要使用会话锁,否则您的长期 运行ning 作业将不得不在事务中 运行。会话锁可能很棘手。很容易得到一个阻塞的进程。无论如何,它可能看起来像这样:

create or alter procedure ThereCanBeOnlyOne @sql nvarchar(max)
as
begin

  --hash the sql batch to generate the app lock name
  declare @lockname nvarchar(255) =  concat('lock_',convert(nvarchar(255), hashbytes('MD5', @sql) , 1));
  print @lockname

  declare @result int
  --request an applock with an immediate timeout
  exec @result = sp_getapplock @Resource=@lockname, @LockMode='Exclusive',@LockOwner='Session',@LockTimeout=0
  if @result = -1 --lock timeout procedure is already running
  begin
     --wait for other session to finish and return
     exec sp_getapplock @Resource=@lockname, @LockMode='Exclusive',@LockOwner='Session';
     exec sp_releaseapplock @Resource=@lockname, @LockOwner='Session';
     print 'batch completed in another session';
     return 0;
  end
  begin try
    --actually run the batch
    exec (@sql);
    exec sp_releaseapplock @Resource=@lockname, @lockOwner='Session';
    print 'batch completed in this session';
    return 0;
  end try
  begin catch
       if (APPLOCK_MODE('public', @lockname, 'Session') = 'Exclusive')
        exec sp_releaseapplock @Resource=@lockname, @lockOwner='Session';
       throw;
  end catch


end