Como reduzo todos os arquivos rapidamente para todos os bancos de dados?

47

No SQL Server (2008, neste caso), como posso reduzir rapidamente todos os arquivos, log e dados, para todos os bancos de dados em uma instância? Eu poderia passar pelo SSMS e clicar com o botão direito do mouse em cada um e escolher Tarefas -> Diminuir, mas estou procurando algo mais rápido.

Eu escrevi alguns scripts "Criar banco de dados" e esqueci que eles tinham tamanhos de balão para os padrões e não precisam de muito espaço reservado para esses arquivos neste projeto.

jcolebrand
fonte

Respostas:

55

Quando você executa "Tarefas -> Reduzir" na GUI, ele emite um DBCC SHRINKDATABASEcomando nos bastidores. Tente. Quando a caixa de diálogo aparecer, não clique no botão "OK". Em vez disso, clique no botão "Script". Você verá o comando em uma janela de consulta. Combine isso com uma consulta em sys.databases (deixe de fora o master e o msdb) e você poderá criar um script para reduzir todos os bancos de dados.

Por exemplo (extraído do comentário de 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;

Copie a saída dessa consulta e execute-a para reduzir todos os seus arquivos.

Larry Coleman
fonte
1
Ok, acho que tenho o que quero (feio, mas faz exatamente o que preciso). 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 > 4Mas descobrir isso me deu um novo problema. Desligado para postar outra pergunta.
Jcebrand
A sério. Confira a resposta de Sandy. o sp_MSForEachDB (há também uma "mesa" sproc, também) são extremamente útil
swasheck
3
E aqui está o lembrete obrigatório para todos que estão lendo isso: Diminuir o banco de dados é perigoso.
31412 Nick Chammas
1
filtrar o banco de dados offline tornaria ainda melhor. :-)
TiloBunt
1
Concordado com @TiloBunt, toda a condição é melhor como WHERE d.database_id> 4 AND d.state_desc = 'ONLINE';
Mauro
23

Que tal uma única linha de instrução sql?

Leia este post de blog muito interessante antes de executar a seguinte instrução sql.

EXEC sp_MSForEachDB 'DBCC SHRINKDATABASE (''?'' , 0)'
CoderHawk
fonte
6
Uma única linha de código não é necessariamente melhor se não funcionar corretamente. Leia também estas postagens, já que o sp_msforeachdb pode ignorar bancos de dados e não avisá-lo: sqlblog.com/blogs/aaron_bertrand/archive/2010/12/29/… e mssqltips.com/sqlservertip/2201/…
Aaron Bertrand
15

DBCC SHRINKDB (e seu primo SHRINKFILE) são extremamente lentos, porque há muita execução de encadeamento único acontecendo nesse código.

Uma maneira muito mais rápida de reduzir um arquivo de banco de dados é:

  • Alocar um novo grupo de arquivos ao banco de dados
  • Torne esse grupo de arquivos o maior possível (use sp_spaceusedpara determinar o tamanho)
  • Recrie todos os índices para este novo grupo de arquivos
  • Solte o antigo grupo de arquivos

Como as recriações de índice são massivamente paralelas, essa técnica geralmente resulta em um encolhimento muito mais rápido do banco de dados. Obviamente, é necessário um espaço extra para o novo grupo de arquivos enquanto o processo está em andamento. No entanto, você só precisa de espaço suficiente no novo grupo de arquivos para armazenar o maior grupo de arquivos da instância (pois estará recuperando espaço à medida que avança).

Essa técnica também possui o benefício adicional de desfragmentar seus índices no processo.

Thomas Kejser
fonte
Você esqueceu uma parte importante. A reconstrução de índices não moverá mais nada, incluindo procedimentos armazenados, exibições, funções, sinônimos, heaps, etc., etc.
Jeff Moden
E eles não ocupam espaço com o qual você deveria se preocupar. Eles precisam residir no grupo de arquivos PRIMARY também, você realmente não pode movê-los (e você também não deveria)
Thomas Kejser
13

Ajustei um pouco a consulta para encolher apenas o LOG conforme solicitado:

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'
Frankachela
fonte
"rapidamente encolher todos os arquivos, tanto de log e de dados"
Dezso
2
Eu estava procurando por isso e estava prestes a postar duas vezes quando vi sua resposta. Resposta não direta, mas MUITO relevante e pontual no meu caso.
Gomibushi
2

O código abaixo, obtém uma lista de bancos de dados que não são do sistema, defina o banco de dados como somente leitura e reduza o arquivo. Eu mantive esse código em algumas caixas do SQL Server usando o SQL Agent Job, onde o espaço é sempre um problema. Na noite de sábado / domingo, toda semana, ele começa a ser executado e reduz todos os bancos de dados em poucas horas (dependendo do tamanho dos bancos de dados).

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
Muhammad Sharjeel Ahsan
fonte
0

Reduza todos os arquivos de log, exceto mestre, modelo, 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'
Emrah Saglam
fonte
0

Este estende a resposta acima, usando um cursor para percorrer as instruções SQL uma a uma. Não é tão curta quanto a resposta de Emrah, mas permite lógica adicional dentro do loop while do cursor.

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
Alistair
fonte
0

Podemos repetir SHRINKDBe SHRINKFILEpara todos os bancos de dados dinamicamente:

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

Você pode encontrar detalhes neste artigo .

Anup Kulkarni
fonte