如何在 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 锁,这只会在您的事务提交或回滚时发生。
我正在远程 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 锁,这只会在您的事务提交或回滚时发生。