在SQL Server(在本例中为2008)中,如何快速缩小实例上所有数据库的所有文件(日志和数据)?我可以遍历SSMS,然后右键单击每个,然后选择“任务”->“收缩”,但是我正在寻找更快的东西。
我编写了一些“创建数据库”脚本的脚本,但忘记了它们具有默认值的膨胀大小,并且在此项目中不需要为这些文件保留太多的空间。
在SQL Server(在本例中为2008)中,如何快速缩小实例上所有数据库的所有文件(日志和数据)?我可以遍历SSMS,然后右键单击每个,然后选择“任务”->“收缩”,但是我正在寻找更快的东西。
我编写了一些“创建数据库”脚本的脚本,但忘记了它们具有默认值的膨胀大小,并且在此项目中不需要为这些文件保留太多的空间。
Answers:
当您从GUI执行“任务->收缩”时,实际上会DBCC SHRINKDATABASE
在后台发出命令。试试吧。出现对话框时,不要单击“确定”按钮。而是单击“脚本”按钮。您将在查询窗口中看到该命令。将其与对sys.databases的查询结合起来(省去master和msdb),然后可以制作一个脚本来收缩所有数据库。
例如(摘自jcolebrand的评论):
SELECT
'USE [' + d.name + N']' + CHAR(13) + CHAR(10)
+ 'DBCC SHRINKFILE (N''' + mf.name + N''' , 0, TRUNCATEONLY)'
+ CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)
FROM
sys.master_files mf
JOIN sys.databases d
ON mf.database_id = d.database_id
WHERE d.database_id > 4;
复制该查询的输出并运行它以缩小所有文件。
DBCC SHRINKDB(及其表亲SHRINKFILE)非常慢,因为该代码中有很多单线程执行。
收缩数据库文件的一种更快的方法是:
sp_spaceused
确定大小)由于索引重建是大规模并行的,因此该技术通常会导致数据库收缩更快。当然,它确实需要您在过程进行过程中为新文件组留一些额外的空间。但是,新文件组中仅需要足够的空间来容纳实例中最大的文件组(因为您将在继续过程中回收空间)。
该技术还具有在过程中对索引进行碎片整理的附加好处。
我调整了一些查询以仅按要求缩小LOG:
set nocount on
SELECT
'USE [' + d.name + N']' + CHAR(13) + CHAR(10)
+ 'DBCC SHRINKFILE (N''' + mf.name + N''' , 0, TRUNCATEONLY)'
+ CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)
FROM
sys.master_files mf
JOIN sys.databases d
ON mf.database_id = d.database_id
WHERE d.database_id > 4 and mf.type_desc = 'LOG'
下面的代码获取非系统数据库的列表,将数据库设置为只读,然后缩小文件。我使用SQL Agent Job将这些代码保存在几个SQL Server框中,其中空间始终是一个问题。每周的周六/周日晚上,它会开始运行,并在几个小时内收缩所有数据库(取决于数据库的大小)。
declare @db varchar(255)
declare c cursor for
select name from sys.databases where is_read_only=0 and state=0
and name not in ('master','model','tempdb','msdb')
open c
fetch c into @db
while @@fetch_status=0
begin
exec SP_dboption @db,'trunc. log on chkpt.','true'
DBCC shrinkdatabase (@db)
fetch next from c into @db
end
close c
deallocate c
收缩除主文件,模型,msdb以外的所有日志文件:
EXEC sp_MSforeachdb '
DECLARE @sqlcommand nvarchar (500)
IF ''?'' NOT IN (''master'', ''model'', ''msdb'')
BEGIN
USE [?]
SELECT @sqlcommand = ''DBCC SHRINKFILE (N'''''' +
name
FROM [sys].[database_files]
WHERE type_desc = ''LOG''
SELECT @sqlcommand = @sqlcommand + '''''' , 0)''
EXEC sp_executesql @sqlcommand
END'
这个扩展了上面的答案,使用游标一个接一个地遍历SQL语句。它不像Emrah的回答那么短,但是它确实允许在游标中的while循环内使用其他逻辑。
SELECT
'USE ['
+ databases.name + N']'
+ CHAR(13)
+ CHAR(10)
+ 'DBCC SHRINKFILE (N'''
+ masterFiles.name
+ N''' , 0, TRUNCATEONLY)'
+ CHAR(13)
+ CHAR(10)
+ CHAR(13)
+ CHAR(10) AS sqlCommand
INTO
#shrinkCommands
FROM
[sys].[master_files] masterFiles
INNER JOIN [sys].[databases] databases ON masterFiles.database_id = databases.database_id
WHERE
databases.database_id > 4; -- Exclude system DBs
DECLARE iterationCursor CURSOR
FOR
SELECT
sqlCommand
FROM
#shrinkCommands
OPEN iterationCursor
DECLARE @sqlStatement varchar(max)
FETCH NEXT FROM iterationCursor INTO @sqlStatement
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXEC(@sqlStatement)
FETCH NEXT FROM iterationCursor INTO @sqlStatement
END
-- Clean up
CLOSE iterationCursor
DEALLOCATE iterationCursor
DROP TABLE #shrinkCommands
我们可以重复SHRINKDB
,并SHRINKFILE
为动态的所有数据库:
while @DBID<=@MaxDBID
begin
-- Used Dynamic SQL for all databases.
Set @SQL ='Use '+@DBName+ ' '+Char(10)
Set @SQL += 'DBCC SHRINKFILE('+@Filename+',5)' +Char(10)
Set @SQL += 'DBCC SHRINKDATABASE('+@DBName+')'+Char(10)
--#6 Increment DBid for looping over all databases
Set @DBID = @DBID+1
Select @DBName = DBName, @Filename=DBFileName from #DBNames where [dbid] = @DBID and type_Desc = 'LOG'
Print (@SQL)
Exec (@SQL)
end
您可以在本文中找到详细信息。
SELECT 'USE [' + d.name + N']' + CHAR(13) + CHAR(10) + 'DBCC SHRINKFILE (N''' + mf.name + N''' , 0, TRUNCATEONLY)' + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10) FROM sys.master_files mf JOIN sys.databases d ON mf.database_id = d.database_id WHERE d.database_id > 4
但是搞清楚了这一点给了我一个新的问题。下发另一个问题。