¿Cómo reduzco todos los archivos rápidamente para todas las bases de datos?

47

En SQL Server (2008 en este caso), ¿cómo puedo reducir rápidamente todos los archivos, tanto de registro como de datos, para todas las bases de datos en una instancia? Podría pasar por SSMS y hacer clic derecho en cada uno y elegir Tareas -> Reducir, pero estoy buscando algo más rápido.

Escribí algunas secuencias de comandos "Crear base de datos" y olvidé que tenían tamaños de globo por defecto, y no necesito tanto espacio reservado para estos archivos en este proyecto.

jcolebrand
fuente

Respuestas:

55

Cuando haces "Tareas -> Reducir" desde la GUI, en realidad emite un DBCC SHRINKDATABASEcomando detrás de escena. Intentalo. Cuando aparezca el cuadro de diálogo, no haga clic en el botón "Aceptar". En su lugar, haga clic en el botón "Script". Verá el comando en una ventana de consulta. Combine eso con una consulta en sys.databases (omita master y msdb), y puede hacer un script para reducir todas las bases de datos.

Por ejemplo (tomado del comentario 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 el resultado de esa consulta y ejecútelo para reducir todos sus archivos.

Larry Coleman
fuente
1
Ok, creo que tengo lo que quiero (feo pero hace exactamente lo que necesito) 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 > 4Pero descubrir eso me dio un nuevo problema. Desactivado para publicar otra pregunta.
jcolebrand
Seriamente. Mira la respuesta de @ Sandy. los sp_MSForEachDB (también hay una "tabla" sproc, también) son extremadamente útiles
swasheck
3
Y aquí está el recordatorio obligatorio para todos los que están leyendo esto: reducir su base de datos es peligroso.
Nick Chammas
1
filtrar fuera de línea DB lo haría aún mejor. :-)
TiloBunt
1
De acuerdo con @TiloBunt, toda la condición es mejor como WHERE d.database_id> 4 AND d.state_desc = 'ONLINE';
Mauro
23

¿Qué tal una sola línea de instrucción SQL?

Lea esta publicación de blog muy interesante antes de ejecutar la siguiente declaración SQL.

EXEC sp_MSForEachDB 'DBCC SHRINKDATABASE (''?'' , 0)'
CoderHawk
fuente
66
Una sola línea de código no es necesariamente mejor si no funciona correctamente. Lea también estas publicaciones, ya que sp_msforeachdb puede omitir bases de datos y no advertirle: sqlblog.com/blogs/aaron_bertrand/archive/2010/12/29/… y mssqltips.com/sqlservertip/2201/…
Aaron Bertrand
15

DBCC SHRINKDB (y su primo SHRINKFILE) son extremadamente lentos, porque hay una gran ejecución de subprocesos en ese código.

Una forma mucho más rápida de reducir un archivo de base de datos es esta:

  • Asignar un nuevo grupo de archivos a la base de datos
  • Haga que este grupo de archivos sea tan grande como debe ser (use sp_spaceusedpara determinar qué tan grande)
  • Reconstruir todos los índices para este nuevo grupo de archivos
  • Suelte el antiguo grupo de archivos

Debido a que las reconstrucciones de índices son masivamente paralelas, esta técnica a menudo resulta en una reducción mucho más rápida de la base de datos. Por supuesto, requiere que tenga un poco de espacio extra para el nuevo grupo de archivos mientras el proceso continúa. Sin embargo, solo necesita suficiente espacio en el nuevo grupo de archivos para contener el grupo de archivos más grande de la instancia (ya que recuperará espacio a medida que avance).

Esta técnica también tiene el beneficio adicional de desfragmentar sus índices en el proceso.

Thomas Kejser
fuente
Olvidaste una parte importante. La reconstrucción de índices no moverá nada más, incluidos procedimientos almacenados, vistas, funciones, sinónimos, montones, etc., etc.
Jeff Moden
Y esos no ocupan espacio que deba preocuparle. TIENEN que residir en el grupo de archivos PRIMARIO también, realmente no puedes moverlos (y tampoco deberías)
Thomas Kejser
13

Ajusté un poco la consulta para reducir solo el LOG como se solicita:

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
fuente
"reducir rápidamente todos los archivos, tanto de registro como de datos"
dezso
2
Estaba buscando esto, y estaba a punto de publicar dos veces cuando vi su respuesta. No es una respuesta directa, pero MUY relevante y acertada para mi caso.
Gomibushi
2

En el siguiente código, obtenga una lista de bases de datos que no sean del sistema, configure la base de datos como de solo lectura y luego reduzca el archivo. He guardado este código en algunos cuadros de SQL Server usando SQL Agent Job, donde el espacio siempre es un problema. Los sábados y domingos por la noche, cada semana, comienza a ejecutarse y reduce todas las bases de datos en pocas horas (dependiendo del tamaño de las bases de datos).

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
fuente
0

Reducir todos los archivos de registro excepto maestro, 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
fuente
0

Éste extiende la respuesta anterior, usando un cursor para recorrer las sentencias SQL una por una. No es tan corto como la respuesta de Emrah, pero permite una lógica adicional dentro del ciclo while dentro del 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
fuente
0

Podemos repetir SHRINKDBy SHRINKFILEpara todas las bases de datos dinámicamente:

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

Puedes encontrar detalles en este artículo .

Anup Kulkarni
fuente