Shrinking the log file script for SQL server database
This script mainly for a DBA’s, because most of the time the log file grow again and again and eats up all the disk space and finally an application goes down.
Errors from SQL server error log:
Error: 9002, Severity: 17, State: 2.
The transaction log for database ‘test’ is full. To find out why space in the log cannot be reused, see the log_reuse_wait_desc column in sys.databases
If you got an error “Error: 9002, Severity: 17, State: 2.” like this go and check the log_reuse_wait_desc column in sys.databases by running below command and see what value it returns.
SELECT name, recovery_model_desc, log_reuse_wait_desc FROM sys.databases name='test'
Shrinking the files is not recommended but, anyway to prevent the database down we can shrink the log files at least some crucial situation. Try to avoid shrinking database files as much as possible. See my previous post How to avoid the shrinking.
--history propose USE [master] GO CREATE TABLE [DBO].[tbl_get_logfiles_details]( [dbname] [SYSNAME] NOT NULL, [backup_location] [SYSNAME]NOT NULL, [name] [SYSNAME] NOT NULL, [log_size] [INT] NULL, [log_usedsize] [INT] NULL, [log_usedsize%] [DECIMAL](18, 0) NULL, [log_reuse_wait_desc] [SYSNAME] NOT NULL, [freespace] [INT] NULL, [shrink_status] [SYSNAME] NULL, [date] [DATETIME] NULL )
Note:
This procedure only works with the following criteria.
- A backup disk can be a local disk.
- The log backup has been done at least one time for the all databases.
- The database recovery model does not simple.
This coding style has been taken from Support-Matrix Brent Ozar websites thanks to Brent.
The 1 GB log file size is rough calculation and you can alter the procedure whatever you want.
Important note: This procedure will not shrink the log file if it has an active transaction (VLF).
CREATE PROCEDURE Usp_dba_shrink_logfiles /* Summary: Shrinking the log files to prevent the disk running out of space Contact: Muthukkumaran Kaliyamoorhty SQL DBA Description: This Sproc will take the each of the log files one by one and check their size more than 1GB then this will do two kinds of work. 1. Check the log filesize >1GB AND log reuse wait type is not log backup then shrink 2. Check the log filesize >1GB AND log reuse wait type is log backup AND the drive size greater than backup size then take a backup AND shrink it ChangeLog: Date Coder Description 2011-03-11 Muthukkumaran Kaliyamoorhty created 2011-04-18 Muthukkumaran Kaliyamoorhty added aditional part for make the VLF to inactive *******************All the SQL keywords should be written in upper case*********************/AS BEGIN SET nocount ON DECLARE @dbname SYSNAME DECLARE @minid INT DECLARE @maxid INT DECLARE @filename SYSNAME DECLARE @log_filesize INT DECLARE @sql SYSNAME DECLARE @sql1 SYSNAME DECLARE @sql2 SYSNAME DECLARE @sql3 SYSNAME DECLARE @sql4 SYSNAME DECLARE @size INT DECLARE @log_used_size INT DECLARE @bak_locatiON SYSNAME DECLARE @freespace INT DECLARE @logwait_type SYSNAME --------------------------------------------------------------------------------------------- --inserting the drive space. --------------------------------------------------------------------------------------------- CREATE TABLE #drive_size ( drive CHAR(1), freespace INT ) INSERT INTO #drive_size EXEC MASTER..Xp_fixeddrives DECLARE @tbl_loop TABLE ( id INT IDENTITY, dbname SYSNAME) INSERT INTO @tbl_loop SELECT Db_name(f.database_id) FROM MASTER.sys.databases d JOIN MASTER.sys.dm_os_performance_counters p ON ( d.name = p.instance_name ) JOIN MASTER.sys.dm_os_performance_counters s ON ( d.name = s.instance_name ) JOIN MASTER.sys.master_files f ON ( d.database_id = f.database_id ) JOIN msdb.dbo.backupset bs ON ( Db_name(f.database_id) = bs.database_name ) JOIN msdb.dbo.backupmediafamily bf ON ( bs.media_set_id = bf.media_set_id ) JOIN #drive_size ds ON ( ds.drive = LEFT(bf.physical_device_name, 1) ) WHERE p.counter_name LIKE 'log file(s) used size (kb)%' AND s.counter_name LIKE 'log file(s) size (kb)%' AND f.type_desc = 'log' AND f.database_id NOT IN( 1, 2, 3, 4 ) AND f.size / 128 > 1024 AND bs.TYPE = 'l' GROUP BY Db_name(f.database_id), f.name, s.cntr_value / 1024, p.cntr_value / 1024, CAST(CAST(p.cntr_value AS FLOAT) / CAST(s.cntr_value AS FLOAT)AS DECIMAL(18, 2)) * 100, d.log_reuse_wait_desc, ds.freespace ORDER BY s.cntr_value / 1024 SELECT @minid = MIN(id) FROM @tbl_loop SELECT @maxid = MAX(id) FROM @tbl_loop --------------------------------------------------------------------------------------------- --Archive the log file size for future reference --------------------------------------------------------------------------------------------- INSERT INTO MASTER.dbo.tbl_get_logfiles_details SELECT Db_name(f.database_id), MAX(bf.physical_device_name), f.name, s.cntr_value / 1024, p.cntr_value / 1024, CAST(CAST(p.cntr_value AS FLOAT) / CAST(s.cntr_value AS FLOAT)AS DECIMAL( 18, 2)) * 100, d.log_reuse_wait_desc, ds.freespace, 'Shrinklog', Getdate() FROM MASTER.sys.databases d JOIN MASTER.sys.dm_os_performance_counters p ON ( d.name = p.instance_name ) JOIN MASTER.sys.dm_os_performance_counters s ON ( d.name = s.instance_name ) JOIN MASTER.sys.master_files f ON ( d.database_id = f.database_id ) JOIN @tbl_loop t ON ( Db_name(f.database_id) = t.dbname ) JOIN msdb.dbo.backupset bs ON ( Db_name(f.database_id) = bs.database_name ) JOIN msdb.dbo.backupmediafamily bf ON ( bs.media_set_id = bf.media_set_id ) JOIN #drive_size ds ON ( ds.drive = LEFT(bf.physical_device_name, 1) ) WHERE p.counter_name LIKE 'log file(s) used size (kb)%' AND s.counter_name LIKE 'log file(s) size (kb)%' AND f.type_desc = 'log' AND f.database_id NOT IN( 1, 2, 3, 4 ) AND f.size / 128 > 1024 AND bs.TYPE = 'l' GROUP BY Db_name(f.database_id), f.name, s.cntr_value / 1024, p.cntr_value / 1024, CAST(CAST(p.cntr_value AS FLOAT) / CAST(s.cntr_value AS FLOAT)AS DECIMAL(18, 2)) * 100, d.log_reuse_wait_desc, ds.freespace ORDER BY s.cntr_value / 1024 WHILE ( @minid <= @maxid ) BEGIN SELECT @dbname = Db_name(f.database_id), @bak_locatiON = MAX(bf.physical_device_name), @filename = f.name, @log_filesize = s.cntr_value / 1024, @size = p.cntr_value / 1024, @log_used_size = CAST(CAST(p.cntr_value AS FLOAT) / CAST( s.cntr_value AS FLOAT) AS DECIMAL(18, 2)) * 100, @logwait_type = d.log_reuse_wait_desc, @freespace = ds.freespace FROM MASTER.sys.databases d JOIN MASTER.sys.dm_os_performance_counters p ON ( d.name = p.instance_name ) JOIN MASTER.sys.dm_os_performance_counters s ON ( d.name = s.instance_name ) JOIN MASTER.sys.master_files f ON ( d.database_id = f.database_id ) JOIN @tbl_loop t ON ( Db_name(f.database_id) = t.dbname ) JOIN msdb.dbo.backupset bs ON ( Db_name(f.database_id) = bs.database_name ) JOIN msdb.dbo.backupmediafamily bf ON ( bs.media_set_id = bf.media_set_id ) JOIN #drive_size ds ON ( ds.drive = LEFT(bf.physical_device_name, 1) ) WHERE p.counter_name LIKE 'log file(s) used size (kb)%' AND s.counter_name LIKE 'log file(s) size (kb)%' AND f.type_desc = 'log' AND f.database_id NOT IN( 1, 2, 3, 4 ) AND f.size / 128 > 1024 AND bs.TYPE = 'l' AND t.id = @minid GROUP BY Db_name(f.database_id), f.name, s.cntr_value / 1024, p.cntr_value / 1024, CAST(CAST(p.cntr_value AS FLOAT) / CAST( s.cntr_value AS FLOAT)AS DECIMAL(18, 2)) * 100, d.log_reuse_wait_desc, ds.freespace ORDER BY s.cntr_value / 1024 --------------------------------------------------------------------------------------------- --Check the log filesize >1GB AND log reuse wait type is not log backup then shrink. --------------------------------------------------------------------------------------------- IF( @log_filesize >= 1024 AND @logwait_type <> 'log_backup' ) BEGIN CHECKPOINT SET @sql='EXEC (''USE[' + @dbname + '];DBCC SHRINKFILE(' + @filename + ')'')' EXEC @sql END --------------------------------------------------------------------------------------------- --Check the log filesize >1GB AND log reuse wait type is log backup AND the drive size -- greater than backup size then take a backup AND shrink it. --------------------------------------------------------------------------------------------- ELSE IF( @log_filesize >= 1024 AND @logwait_type = 'log_backup' AND @log_filesize < @freespace ) BEGIN SET @sql1='BACKUP LOG [' + @dbname + '] TO DISK=''' + @bak_locatiON + '''' EXEC @sql1 EXEC @sql1 SET @sql2='EXEC (''USE[' + @dbname + '];DBCC SHRINKFILE(' + @filename + ')'')' EXEC @sql2 UPDATE MASTER.dbo.tbl_get_logfiles_details SET shrink_status = 'Shrink&Bak' END --------------------------------------------------------------------------------------------- --If the first two conditions are not reduce the log file size because of active VLF, --then take a log backup again and mark the active VLF to inactive VLF then shrink it. --------------------------------------------------------------------------------------------- ELSE IF( @log_filesize >= 1000 AND @log_filesize < @freespace ) BEGIN SET @sql3='BACKUP LOG [' + @dbname + '] TO DISK=''' + @bak_locatiON + '''' EXEC @sql3 EXEC @sql3 SET @sql4='EXEC (''USE[' + @dbname + '];DBCC SHRINKFILE(' + @filename + ')'')' EXEC @sql4 UPDATE MASTER.dbo.tbl_get_logfiles_details SET shrink_status = 'Shrink&Bak_3' END SET @minid=@minid + 1 END END