如何在 SqlServer 中的事务中执行 ALTER DATABASE

M.S*_*baa 5 t-sql sql-server transactions

我正在远程 sql server 数据库上做一些工作,这需要一些时间,我需要阻止任何其他连接到它,这样就不会丢失数据我相信我应该使用单用户模式来执行此操作

我需要在完成工作后将其恢复到多用户模式,但是我与远程服务器的连接不可靠,很多时候会在完成之前断开连接,通常只是自动回滚并稍后再执行,问题是当我尝试在事务中执行它我收到此错误:

多语句事务中不允许使用 ALTER DATABASE 语句

我该如何表现

ALTER DATABASE dbName
SET SINGLE_USER WITH ROLLBACK IMMEDIATE
Run Code Online (Sandbox Code Playgroud)

在事务中并确保在断开连接时它将回滚到多用户模式?

Dam*_*ver 2

因此,如果我们的连接断开,我们会尝试安排数据库返回到多用户模式。这是一种可行的方法,但它和罪恶一样丑陋。

首先,我们进行适当的设置:

create database RevertTest
go
use master
go
create table RevertLock (L int not null)
go
declare @rc int
declare @job_id uniqueidentifier

exec @rc = msdb..sp_add_job @job_name='RevertSingleUser',
                            @description='Revert the RevertTest database to multi_user mode',
                            @delete_level=3,
                            @job_id = @job_id OUTPUT

if @rc != 0 goto Failed

exec @rc = msdb..sp_add_jobstep @job_id = @job_id,
                                @step_name = 'Wait to revert',
                                @command = '
WHILE EXISTS (SELECT * FROM RevertLock)
    WAITFOR DELAY ''00:00:01''

ALTER DATABASE RevertTest set multi_user

DROP TABLE RevertLock'
if @rc != 0 goto Failed

declare @nowish datetime
declare @StartDate int
declare @StartTime int

set @nowish = DATEADD(minute,30,GETDATE())
select @StartDate = DATEPART(year,@nowish) * 10000 + DATEPART(month,@nowish) * 100 + DATEPART(day,@nowish),
        @StartTime = DATEPART(hour,@nowish) * 10000 + DATEPART(minute,@nowish) * 100 + DATEPART(second,@nowish)

exec @rc = msdb..sp_add_jobschedule @job_id = @job_id,
                                    @name='Failsafe',
                                    @freq_type=1,
                                    @active_start_date = @StartDate,
                                    @active_start_time = @StartTime
if @rc != 0 goto Failed

exec @rc = msdb..sp_add_jobserver @job_id = @job_id
if @rc != 0 goto Failed

print 'Good to go!'
goto Fin
Failed:
print 'No good - couldn''t establish rollback plan'
Fin:
Run Code Online (Sandbox Code Playgroud)

基本上,我们创造了一份可以在我们身后打扫卫生的工作。我们安排工作在半小时内开始运行,但这只是为了保护我们免受小型比赛的影响。

现在,我们运行实际的脚本来完成我们希望它完成的工作:

use RevertTest
go
alter database RevertTest set single_user with rollback immediate
go
begin transaction
go
insert into master..RevertLock(L) values (1)
go
exec msdb..sp_start_job @job_name='RevertSingleUser'
go
WAITFOR DELAY '01:00:00'
Run Code Online (Sandbox Code Playgroud)

如果运行此脚本,您将能够观察到数据库已进入单用户模式 ​​- 最后WAITFOR DELAY只是为了模拟我们“做工作” - 无论您想在数据库中做什么在单用户模式下。如果停止此查询运行断开此查询窗口,在一秒钟内您应该会看到数据库已返回到multi_user模式。

要成功完成脚本,只需将最后一个任务(之前COMMIT)设置为从RevertLock表中删除即可。就像断开连接一样,恢复作业1将负责将数据库切换回multi_user,然后自行清理。


1这份工作实际上有点欺骗性。它实际上不会循环并检查 master 中的表 - 因为您的事务由于INSERT. 相反,它会耐心等待获取合适的锁,这仅在事务提交或回滚时发生。