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
16 Comments
Anonymous
Muthu,
Its nice one.
Is there any reason to run the log backup two times or you forget to remove it
EXEC @sql1
EXEC @sql1
—
Ram
Muthukkumaran Kaliyamoorthy
Ram,
Thanks my dear friend for checking my blog scripts.
Nope it depends,
sometimes the shrinking will not shrink the log files if it has active VLF to making the VLF's to inactive. I have run the log backup twice (maybe it depends)
Jagadish
I shrink the database log file by the suggestion given by the query select name, log_reuse_wait_desc from sys.databases. if it has log backup as a field, I go with log backup. Often I used to take twice log backup to shrink, then shrink happens. I’m not clear why we go for second log backup. It is been a question mark for me since long time. Could you please explain
Muthukkumaran kaliyamoorthy
Hi Jagadish,
As long as, you do not have any active VLF in the file you can shrink. The twice is “it depends”. It may reduce or increase. Sometimes a single log backup will write all the active VLF to the data file.
vinoth
Muthu,
Excellent script.Very well written i like this procedure.
Anonymous
Hi,
Definitely it's good script even though its not shrinking the log file fully.In history table its 'nothing', If i take a log backup once again its shrinking.
http://social.msdn.microsoft.com/Forums/en/sqldatabaseengine/thread/9f978fc7-0dcb-4035-b96d-1b58a81e3b1f
Muthukkumaran Kaliyamoorthy
@Anonymous,
Yep May be your log file have active transaction that's why you can't shrink the file, nothing with the log_reuse_wait_desc. Log Backup may change the active vlf to inactive vlf.
See my twitter link Paul randal has replied regarding this.
http://twitter.com/#!/mentions
Muthukkumaran Kaliyamoorthy
@vinoth Thanks This style has taken from Brent site.
Muthukkumaran Kaliyamoorthy
@Anonymous,
Now i added the 3rd-condition.
EXEC @sql3
If the first 2 conditions are not shrink the log file ,it will check the log file size > 1000 then make a log backup once again and shrink it.
This will definitely shrink if it shows log_reuse_wait_desc="nothing".Thanks for your reply.
Anonymous
Muthu
I went for vacation da.I know about you.as i am the first person to check yours (:-
Ram
Muthukkumaran
Hey ram i know… i know.
Thanks man.
Ladainian
I told my grandmother how you hpeled. She said, bake them a cake!
Pingback:
Marielle Baza
Keep working ,impressive job!
Mary Ann
$author Thank you so much for a wonderful blog. It was such a great article. Have a great day!
priyanshi
hi muthu
what to change in script if i do not have backupmedia locally , i want to have backup log to disk=’nul’ also with compression