
When having Microsoft SQL databases, its log files can grow quite a bit, potentially slow down the database server and eat up disk space.
To shrink a database, one can run the following line:
EXEC D_ShrinkDBLogs 0,100,1000,'with truncate_only','DB_NAME'
(change DB_NAME with the database’s name)
To shrink all databases, one can use “sp_MSforeachdb” which is an undocumented sql stored procedure:
EXEC sp_MSForEachDB 'D_ShrinkDBLogs 0,100,1000,''with truncate_only'',''?'''
To run this, the following stored procedure need to be installed. It can just be copy pasted from the code below (or download from this link):
CREATE PROC [dbo].[D_ShrinkDBLogs]
@target_percent TINYINT = 0, @target_size_MB INT, @max_iterations INT, @trunc NVARCHAR(1000),
@DB VARCHAR(30)
AS
SET nocount ON
SET @trunc = 'with truncate_only'
--SET @trunc = ''
DECLARE
@last_row INT, @log_size DECIMAL(15,2), @unused_at_start DECIMAL(15,2),
@unused DECIMAL(15,2), @shrinkable DECIMAL(15,2), @iteration INT, @file_max INT,
@file INT, @fileid VARCHAR(5)
SELECT @iteration = 0
PRINT @DB
CREATE TABLE #loginfo
(id INT IDENTITY, FileId INT, FileSize NUMERIC(22,0), StartOffset NUMERIC(22,0),
FSeqNo INT, Status INT, Parity SMALLINT, CreateLSN NUMERIC(30) )
CREATE TABLE #logfiles
(id INT IDENTITY(1,1), fileid VARCHAR(5) NOT NULL )
INSERT #logfiles
( fileid )
SELECT CONVERT(VARCHAR,fileid) FROM sysfiles WHERE status = 1048642
SELECT @file_max = @@rowcount
IF OBJECT_ID('shrinktab') IS NULL
EXEC ('create table shrinktab ( x nchar(3000) not null )')
INSERT #loginfo
(
FileId,FileSize,StartOffset,FSeqNo,Status,
Parity,CreateLSN )
EXEC ('dbcc loginfo (' + @DB + ')'
)
SELECT @last_row = @@rowcount
PRINT @last_row
SELECT
@log_size = SUM(FileSize) / 1048576.00,
@unused = SUM(CASE WHEN Status = 0
THEN FileSize ELSE 0 END) / 1048576.00,
@shrinkable = SUM(CASE WHEN id < @last_row - 1 AND Status = 0
THEN FileSize
ELSE 0
END) / 1048576.00
FROM #loginfo
SELECT @unused_at_start = @unused -- save for later
SELECT 'iteration' = @iteration, 'log size, MB' = @log_size,
'unused log, MB' = @unused, 'shrinkable log, MB' = @shrinkable,
'shrinkable %' = CONVERT(DECIMAL(6,2), @shrinkable * 100 / @log_size)
WHILE @shrinkable * 100 / @log_size > @target_percent
AND @shrinkable > @target_size_MB
AND @iteration < @max_iterations
BEGIN
SELECT @iteration = @iteration + 1 -- this is just a precaution
EXEC ('insert shrinktab select name from sysobjects delete shrinktab')
SELECT @file = 0
WHILE @file < @file_max
BEGIN
SELECT @file = @file + 1
SELECT @fileid = fileid FROM #logfiles WHERE id = @file
EXEC
('use ' + @DB + '; dbcc shrinkfile( ' + @fileid + ' )')
END
EXEC
( 'backup log ' + @db + ' ' + @trunc )
TRUNCATE TABLE #loginfo
INSERT #loginfo
( FileId,FileSize,StartOffset, FSeqNo,Status,Parity,CreateLSN )
EXEC ( 'dbcc loginfo' )
SELECT @last_row = @@rowcount
SELECT @log_size = SUM(FileSize) / 1048576.00,
@unused = SUM(CASE WHEN Status = 0 THEN FileSize
ELSE 0
END) / 1048576.00,
@shrinkable = SUM(CASE WHEN id < @last_row - 1
AND Status = 0 THEN FileSize
ELSE 0
END)
/ 1048576.00
FROM #loginfo
SELECT 'iteration' = @iteration, 'log size, MB' = @log_size,
'unused log, MB' = @unused, 'shrinkable log, MB' = @shrinkable,
'shrinkable %' = CONVERT(DECIMAL(6,2),@shrinkable
* 100 / @log_size)
END
SELECT CONVERT(VARCHAR,@iteration) + ' iterations. Log shrunk from ' +
CONVERT(VARCHAR,@unused_at_start) + ' MB to ' + CONVERT(VARCHAR,@unused) + ' MB'
EXEC ( 'drop table shrinktab' )
Note that this procedure wasn't written by me, but ramassed together by a good friend of mine.
Thank you,
Michael
Leave a Reply