如何在 Sql Server 的事务中执行 ALTER DATABASE

How to perform ALTER DATABASE within a TRANSACTION In SqlServer

我正在远程 sql 服务器数据库上做一些工作,这需要一些时间,我需要阻止与它的任何其他连接,以免数据丢失 我相信我应该使用单用户模式来做到这一点

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

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

我如何执行

ALTER DATABASE dbName
SET SINGLE_USER WITH ROLLBACK IMMEDIATE

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

您不能在事务中包含 ALTER 语句。但是您可以像这样置顶和拖尾您的交易:

ALTER DATABASE TEST SET SINGLE_USER 
GO

    BEGIN TRANSACTION

        -- Generate an error.
        SELECT 1/0


    ROLLBACK TRANSACTION

GO
ALTER DATABASE TEST SET MULTI_USER 

此脚本将数据库设置为单用户模式。然后遇到错误,才回到多用户模式。

因此,如果我们的连接断开,我们将尝试安排数据库返回 multi_user 模式。这是 有效的一种方法 ,但它和罪恶一样丑陋。

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

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:

基本上,我们创建了一个在我们之后整理的工作。我们安排作业在半小时后 运行ning 开始,但这只是为了保护我们免受小规模竞争。

我们现在 运行 我们的实际脚本来完成我们想要的工作:

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'

如果你运行这个脚本,你可以观察到数据库进入了单用户模式 ​​- 最后的WAITFOR DELAY只是为了模拟我们"doing work" - 在数据库处于单用户模式时,无论您想在数据库中做什么。如果您停止此查询 运行ning 并且 断开此查询 window,您应该会在一秒钟内看到数据库已返回到 multi_user 模式。

要成功完成您的脚本,只需将最后一个任务(COMMIT 之前)从 RevertLock table 中删除即可。与断开连接一样,还原作业1 将负责将数据库切换回multi_user,然后自行清理。


1作业其实略带欺骗性。它实际上不会循环并检查 master 中的 table - 因为由于 INSERT,您的事务对其具有独占锁定。相反,它会耐心地等待获取 suitable 锁,这只会在您的事务提交或回滚时发生。