如何将 DBCC SHRINKFILE 的结果保存到表中?

Mar*_*lli 5 sql-server dbcc pivot sqlcmd xp-cmdshell

我有以下脚本:

-- available space in each of the database files

PRINT @@SERVERNAME
--SQLDWDEV01
USE [BodenStage]
GO
SELECT name 
,(CAST(ROUND((size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0),2) AS NUMERIC(18,2))) AS AvailableSpaceInMB  
,(CAST(ROUND((size/128.0 - FILEPROPERTY(name, 'SpaceUsed')/128.0)/1024.00,2) AS NUMERIC(18,2))) AS AvailableSpaceInGB 
FROM sys.database_files;  
GO
Run Code Online (Sandbox Code Playgroud)

尽管我不喜欢既不收缩数据也不收缩日志文件,但有时需要这样做。不是我的错。

有方法,以缩小数据文件,而无须描述累积碎片在这里

仅截断

将文件末尾的所有可用空间释放给操作系统,但不会在文件内执行任何页面移动。数据文件仅收缩到最后分配的范围。

如何将DBCC SHRINKFILE的结果保存到表中?

请参见下文,这是将DBCC SQLPERF的结果保存到表中的一种方法。

select @@servername as [Server]
       ,db_name() as [database]
go

SET NOCOUNT ON

begin try
  drop table #radhe
end try
begin catch
end catch


create table #radhe (
  DatabaseName varchar(100)
  , LOGSIZE_MB NUMERIC(18, 9)
  , LOGSPACE_USED NUMERIC(18, 9)
  , LOGSTATUS NUMERIC(18, 9)) 


INSERT #Radhe(DatabaseName, LOGSIZE_MB, LOGSPACE_USED, LOGSTATUS) 
EXEC('DBCC SQLPERF(LOGSPACE);')


select  DatabaseName, 
     LOGSIZE_GB=CONVERT(NUMERIC(18,2) ,ISNULL( ROUND(LOGSIZE_MB/1024.00,2),0)),
     PERC_USED =CONVERT(NUMERIC(4,2) ,ISNULL( ROUND(LOGSPACE_USED,2),0))
from #radhe
order by logsize_mb desc
Run Code Online (Sandbox Code Playgroud)

现在我生成 DBCC SHRINKFILE 命令:

DECLARE @db VARCHAR(108)
DECLARE @dbid INT
DECLARE @amount INT

SELECT @db = 'MY_DATABASSEStage'
SELECT @dbid = DB_ID(@DB) 
SELECT @amount = 0

SELECT SHRINKFILE_SCRIPT = 'USE ' + QUOTENAME( DB_NAME(smf.database_id)) + CHAR(10) 
                         + 'GO' + CHAR(10)  
                         + 'DBCC SHRINKFILE(''' + smf.name + '''' + ',' + CAST(@amount AS VARCHAR) + ', TRUNCATEONLY) WITH NO_INFOMSGS' + CHAR(10) 
                         + 'GO' + CHAR(10)  
, * 
FROM  sys.master_files smf
WHERE smf.database_id = @dbid
Run Code Online (Sandbox Code Playgroud)

上面的脚本产生了以下结果:

USE [MY_DATABASSEStage]
GO
DBCC SHRINKFILE('MY_DATABASSEStageStockLedger',0, TRUNCATEONLY) WITH NO_INFOMSGS
GO

USE [MY_DATABASSEStage]
GO
DBCC SHRINKFILE('MY_DATABASSEStageStockLedgerArchive',0, TRUNCATEONLY) WITH NO_INFOMSGS
GO


USE [MY_DATABASSEStage]
GO
DBCC SHRINKFILE('MY_DATABASSEStageProduct',0, TRUNCATEONLY) WITH NO_INFOMSGS
GO
Run Code Online (Sandbox Code Playgroud)

这是其中一个脚本的结果 - 删除 with no_infomsgs 在此处输入图片说明

有类似的问题,但他们没有给出适用于我的案例的答案:

在 SQL Server 中的用户事务之间收缩日志

保存 DBCC ShrinkFile 的结果

结论

我的答案工作正常,但 srutzky 使用的方法要简单得多并且可以完成工作,只需要一个链接服务器。

对于我的测试服务器,我使用以下脚本来创建链接服务器:

USE [master]
GO

IF NOT EXISTS (SELECT srv.name FROM sys.servers srv WHERE srv.server_id != 0 AND srv.name = N'TESTDBCC')
BEGIN
        EXEC master.dbo.sp_addlinkedserver 
                                            @server = N'TESTDBCC', 
                                            @srvproduct=N'', 
                                            @provider=N'SQLNCLI11', 
                                            @datasrc=N'JPB01275\SQL2014', 
                                            @catalog=N'radhe'


        EXEC master.dbo.sp_addlinkedsrvlogin 
                                            @rmtsrvname=N'TESTDBCC',
                                            @useself=N'True',
                                            @locallogin=NULL,
                                            @rmtuser=NULL,
                                            @rmtpassword=NULL
END
GO

EXEC master.dbo.sp_serveroption 
                                @server=N'TESTDBCC', 
                                @optname=N'collation compatible', 
                                @optvalue=N'false'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'data access', 
                                @optvalue=N'true'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'dist', 
                                @optvalue=N'false'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'pub', 
                                @optvalue=N'false'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'rpc', 
                                @optvalue=N'true'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'rpc out', 
                                @optvalue=N'true'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'sub', 
                                @optvalue=N'false'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'connect timeout', 
                                @optvalue=N'0'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'collation name', 
                                @optvalue=null
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'lazy schema validation', 
                                @optvalue=N'false'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'query timeout', 
                                @optvalue=N'0'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'use remote collation', 
                                @optvalue=N'true'
GO

EXEC master.dbo.sp_serveroption @server=N'TESTDBCC', 
                                @optname=N'remote proc transaction promotion', 
                                @optvalue=N'false'
GO
Run Code Online (Sandbox Code Playgroud)

并使用上面的链接服务器,我可以按如下方式收集数据:

--Use the Linked Server via EXEC() AT

CREATE TABLE #ShrinkFileResults
(
    [DBID] SMALLINT,
    [FileID] INT,
    [CurrentSize] INT,
    [MinimumSize] INT,
    [UsedPages] INT,
    [EstimatedPages] INT
);

INSERT INTO #ShrinkFileResults
  ([DBID], [FileID], [CurrentSize], [MinimumSize], [UsedPages], [EstimatedPages])
  EXEC(N'
     USE [tempdb];
     DBCC SHRINKFILE(N''tempdev'', 0, TRUNCATEONLY);
  ') AT [TestDBCC];


INSERT INTO #ShrinkFileResults
  ([DBID], [FileID], [CurrentSize], [MinimumSize], [UsedPages], [EstimatedPages])
  EXEC(N'
     USE [radhe];
     DBCC SHRINKFILE(N''radhe_log'', 0, TRUNCATEONLY);
  ') AT [TestDBCC];

INSERT INTO #ShrinkFileResults
  ([DBID], [FileID], [CurrentSize], [MinimumSize], [UsedPages], [EstimatedPages])
  EXEC(N'
     USE [US16HSMMProduct_AFTER_CHANGES];
     DBCC SHRINKFILE(N''US16HSMMProduct_AFTER_CHANGES_log'', 0, TRUNCATEONLY);
  ') AT [TestDBCC];

INSERT INTO #ShrinkFileResults
  ([DBID], [FileID], [CurrentSize], [MinimumSize], [UsedPages], [EstimatedPages])
  EXEC(N'
     USE [US16HSMMProduct_AFTER_CHANGES];
     DBCC SHRINKFILE(N''US16HSMMProduct_AFTER_CHANGES_log'', 0, TRUNCATEONLY);
  ') AT [TestDBCC];

SELECT * FROM #ShrinkFileResults;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

Sol*_*zky 3

'remote proc transaction promotion'这可以通过使用属性设置为 的“环回”链接服务器来轻松完成false,这可以通过回避操作启动的隐式事务来避免“无法在用户事务内执行收缩文件操作”INSERT...EXEC错误。否则链接服务器只需要将该'rpc out'属性设置为true。和属性不需要启用(至少对我来说不是)'data access''rpc'

创建链接服务器

USE [master];

DECLARE @LinkedServerName sysname;
SET @LinkedServerName = N'TestDBCC';

IF (NOT EXISTS (SELECT srv.name
                FROM   sys.servers srv
                WHERE  srv.server_id <> 0
                AND    srv.name = @LinkedServerName))
BEGIN
  EXEC [master].dbo.sp_addlinkedserver
    @server = @LinkedServerName, @srvproduct=N'', @provider=N'SQLNCLI11', @datasrc=N'DALI';
  EXEC [master].dbo.sp_addlinkedsrvlogin
    @rmtsrvname=@LinkedServerName,@useself=N'True',@locallogin=NULL,
    @rmtuser=NULL,@rmtpassword=NULL;
END;

--EXEC [master].dbo.sp_serveroption @server=@LinkedServerName,
--  @optname=N'data access', @optvalue=N'true';
--EXEC [master].dbo.sp_serveroption @server=@LinkedServerName,
--  @optname=N'rpc', @optvalue=N'true'; -- is_remote_login_enabled
EXEC [master].dbo.sp_serveroption @server=@LinkedServerName,
  @optname=N'rpc out', @optvalue=N'true';
EXEC [master].dbo.sp_serveroption @server=@LinkedServerName,
  @optname=N'remote proc transaction promotion', @optvalue=N'false';
GO
Run Code Online (Sandbox Code Playgroud)

通过以下方式使用链接服务器EXEC() AT

CREATE TABLE #ShrinkFileResults
(
    [DBID] SMALLINT,
    [FileID] INT,
    [CurrentSize] INT,
    [MinimumSize] INT,
    [UsedPages] INT,
    [EstimatedPages] INT
);

INSERT INTO #ShrinkFileResults
  ([DBID], [FileID], [CurrentSize], [MinimumSize], [UsedPages], [EstimatedPages])
  EXEC(N'
     USE [tempdb];
     DBCC SHRINKFILE(N''tempdev'', 0, TRUNCATEONLY);
  ') AT [TestDBCC];

SELECT * FROM #ShrinkFileResults;
Run Code Online (Sandbox Code Playgroud)

这对我在 SQL Server 2012 SP3 和 SQL Server 2016 RTM 上都有效。

笔记

  • 使用OPENQUERY不起作用。最初,您会收到“元数据发现”错误,可以通过将调用DBCC包装在另一个EXEC使用该WITH RESULT SETS选项的调用中来解决该错误。但随后您会收到有关“OPENQUERY 无法处理对象,因为它不存在或您没有权限”的错误。但是,如果您更改要收缩的文件的逻辑名称,则会收到一条错误DBCC,指出它无法找到该名称的文件,并且会出现一个二级错误,OPENQUERY指出该调用未返回任何结果集,所有这些表示使用正确的文件名会更进一步。

  • 此操作也可以通过 SQLCLR 相当简单地完成。事实上,可以使用外部连接(而不是上下文连接)创建 UDF/标量函数,这将允许在列表中基于集合的操作中进行调用SELECTxp_cmdshell与启用或创建链接服务器相比,这也只需要几行代码,并且可以最大限度地降低安全风险。