SQL Server USE 忽略 IF 块

use*_*162 1 mirroring sql-server-2012 if-not-exists

我想使用维护计划缩小多个数据库的事务日志文件。这些数据库是镜像的,因此无法在MIRROR数据库上执行收缩任务,但必须在两台服务器上都配置维护计划。我正在SQL Server 2012 Standard (SP1)我们的开发系统上使用,但代码也需要处理2008 R2

我试着用下面的脚本,它可以处理一个数据库,称为DR内的,Execute T-SQL Statement Task

IF EXISTS (SELECT NULL FROM sys.databases WHERE [name] = 'DR' AND [state_desc] = 'ONLINE')
BEGIN
    PRINT 'test';
    USE DR;
    DBCC SHRINKFILE (DR_log, 2048);
END
GO
Run Code Online (Sandbox Code Playgroud)

这在 上运行良好PRINCIPLE,但是会导致在 上出现以下错误消息MIRROR

Msg 954, Level 14, State 1, Line 4
The database "DR" cannot be opened. It is acting as a mirror database.
Run Code Online (Sandbox Code Playgroud)

IFUSE命令似乎忽略了该块。本Print如果不执行USE被注释掉。Shrink DatabaseSSMS 中可用的维护任务提供了一个选项ignore databases where the state is not online。但是,日志文件没有显示这是如何完成的。无法使用此任务,因为它还会缩小数据文件,而不仅仅是日志文件。

如何确保USE只有在数据库处于Online状态时才执行?我能想到的另一个选择是使用动态 SQL,我想避免这种情况。

注意:此任务不会频繁执行,而是在导致日志文件超出其通常大小的异常大事务之后执行。

解决方案:我们使用动态 sql 来解决此问题,另请参阅@Aaron Bertrand 提供的答案

Aar*_*and 5

这是在解析时发生的,您几乎无法改变实际行为。SQL Server 对批处理中的语句进行一些检查,并且出于所有意图和目的,假装您的条件逻辑不存在。

出于同样的原因,您不能使用条件逻辑来确定要创建两个可能的 #temp 表中的哪一个(此问题的最常见症状)。尽管将其视为人类,您会发现只能#t创建一个版本,但 SQL Server 并不这么认为。无论您选择Parse( Ctrl+ F5) 还是Execute(just F5),它都会报告错误消息:

IF (1=1)
BEGIN
  CREATE TABLE #t(a INT);
END
ELSE
BEGIN
  CREATE TABLE #t(b INT);
END
Run Code Online (Sandbox Code Playgroud)

两种情况下的结果:

消息 2714,级别 16,状态 1,第 7 行
数据库中已经有一个名为“#t”的对象。

当您所做的只是解析批处理时,错误消息就没有什么意义了;显然#t还不能存在,但它表明 SQL Server 正在评估批处理中的语句,但忽略了那些处理条件逻辑的语句。

为了解决这个问题,你可以使用动态 SQL,它在执行之前不会被解析:

IF EXISTS (SELECT NULL FROM sys.databases WHERE [name] = 'DR' AND [state_desc] = 'ONLINE')
BEGIN
    EXEC DR.sys.sp_executesql N'DBCC SHRINKFILE (DR_log, 2048);';
END
Run Code Online (Sandbox Code Playgroud)

然而,这对我来说似乎真的,真的,真的错了。您是否在不断缩小您的日志文件?为什么?这是一个如此自我挫败的过程。请完整阅读这篇文章:

为什么事务日志不断增长或空间不足?

然后,适当地调整日志文件的大小 - 一次 - 并确保您使用正确的恢复模型,这样您就不会不断地进行这种增长-收缩-增长-收缩-增长-收缩循环。如果日志对于镜像来说太大,请获取更大的磁盘。削弱初级以弥补镜子的不足似乎是不对的。