Как я могу заставить SQL Server 2019 делать вид, что сохраненная pro c выполняется? - PullRequest
0 голосов
/ 19 июня 2020

У меня есть хранимая процедура, которая обновляет несколько таблиц в моей базе данных отчетов из моей действующей производственной базы данных.

Я запускаю:

EXEC Reporting.dbo.RefreshTemps

Этот сохраненный pro c очень прост, все, что он делает, - это усекает несколько таблиц, а затем выполняет INSERT INTO для получения обновленного снимка данных. Для завершения требуется около 10 минут, поскольку это довольно короткие таблицы.

Однако многие люди, процессы или другие процедуры могут захотеть использовать эту процедуру в течение дня. Вполне вероятно, что пользователь A может запустить pro c, а затем пользователь B запустит тот же pro c до завершения запуска пользователя A.

Это будет означать, что когда пользователь A pro c завершится, пользователь B pro c будет в процессе усечения и обновления тех же таблиц. Что было бы плохо.

В идеале, SQL не будет запускать Pro пользователя B c, дождитесь завершения запуска пользователя A, и пользователь A и пользователь B будут одновременно доступны для обновленных таблиц время.

Есть ли способ обмануть SQL, заставив думать, что pro c пользователя B работает нормально, но на самом деле он ожидает завершения запуска пользователя A?

1 Ответ

1 голос
/ 19 июня 2020

Здесь проще всего использовать задание SQL Server Agent. Вы можете запустить задание с помощью sp_start_job , и только одна копия задания может быть запущена в любой момент.

Как и Дэн Гузман предложил, вы также можете использовать sp_getapplock. Но вы, вероятно, захотите использовать блокировки сеанса, иначе ваше длительное задание должно было бы выполняться в транзакции. А блокировка сеанса может быть сложной задачей. Заблокировать процесс легко. В любом случае это может выглядеть примерно так:

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
...