Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Data Management

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

    Search

    XML Feeds

    Google Ads

    « Windows Azure, SQL Azure and .NET Services pricing announcedHow to get data out of Microsoft Dynamics Great Plains »
    comments

    A recent post in the forum I frequent presented an interesting problem - shrink log file in all user databases. See the reasons why it may not be a good idea explained in Do not truncate your LDF files and Why you want to be restrictive with shrink of database files.

    The first idea that came to mind was to use sp_MSForEachDB non-documented stored procedure for this task. You can find this article by Arshad Ali very helpful in understanding sp_MsForEachTable and sp_MSForEachDB stored procedures and their parameters.

    Follow up:

    UPDATE
    Based on Duncan's comments the better code than I suggested would be

    1. sp_MSForEachDb 'IF ''?'' NOT IN (''master'', ''tempdb'', ''tempdev'', ''model'', ''msdb'')
    2. AND (SELECT recovery_model FROM master.sys.databases WHERE name = ''?'') = 1
    3. AND (SELECT is_read_only FROM master.sys.databases WHERE name = ''?'') = 0
    4. BEGIN
    5. declare @LogFile nvarchar(2000)
    6. USE [?]
    7. SELECT @LogFile = sys.database_files.name
    8. FROM sys.database_files
    9. WHERE (sys.database_files.type = 1)
    10. PRINT @LogFile
    11. EXEC(''ALTER DATABASE [?] SET RECOVERY SIMPLE'')
    12. DBCC SHRINKFILE (@LogFile, 1)
    13. EXEC(''ALTER DATABASE [?] SET RECOVERY FULL'')
    14. END'

    It may be even a better idea of using dynamic SQL and implicit looping through databases instead of undocumented SP, like this

    1. declare @SQL nvarchar(max)
    2.  
    3.   select @SQL = coalesce(@SQL + char(13) + char(10),'') + N'
    4. Use ' + QUOTENAME(d.[name]) + ';' + CHAR(13) + '
    5. ALTER DATABASE ' + QUOTENAME(d.[name]) + ' SET RECOVERY SIMPLE;
    6. DBCC SHRINKFILE (' + quotename(mf.[name],'''') + ', 1);
    7. ALTER DATABASE ' + QUOTENAME(d.[name]) + ' SET RECOVERY FULL;'
    8. FROM sys.databases d
    9. INNER JOIN sys.master_files mf ON [d].[database_id] = [mf].[database_id]
    10. WHERE
    11.     d.[database_id] > 4 --no sys dbs
    12.     AND d.recovery_model = 1
    13.     AND d.is_read_only = 0
    14.     AND mf.[type] = 1 --log files
    15. ORDER BY d.name
    16.  
    17. --print @SQL
    18.  
    19. execute (@SQL)

    (code based on this MSDN thread)
    -------------------------
    Interestingly, this solution, proposed by Borislav Borissov, still attempts to change recovery model in TempDB - not clear why:

    1. sp_MSForEachDb 'IF LOWER(''?'') NOT IN (''master'', ''tempdb'', ''tempdev'', ''model'', ''msdb'')
    2.                 BEGIN
    3.                     declare @LogFile nvarchar(2000)
    4.                     declare @ExeString nvarchar(2000)
    5.                     USE [?]
    6.                     SELECT @LogFile = sys.sysaltfiles.name
    7.                            FROM sys.sysdatabases
    8.                     INNER JOIN sys.sysaltfiles ON sys.sysdatabases.dbid = sys.sysaltfiles.dbid
    9.                     WHERE (sys.sysaltfiles.fileid = 1) AND (sys.sysdatabases.name = ''?'')
    10.                     print @LogFile
    11.                     ALTER DATABASE [?] SET RECOVERY SIMPLE
    12.                     DBCC SHRINKFILE (@LogFile, 1)
    13.                     ALTER DATABASE [?] SET RECOVERY FULL
    14.                 END'

    However, the solution suggested by George Mastros of first creating a stored procedure and then executing it, works fine

    1. sp_msforeachdb 'If ''?'' Not In (''master'',''tempdb'',''model'',''msdb'')
    2.      Begin
    3.            Declare @LogFile nvarchar(2000)
    4.            Select @LogFile = Name From master..sysaltfiles Where db_name(dbid) = ''?'' And Fileid = 2
    5.            Set @LogFile = ''Create Procedure dbo.ShrinkMe As
    6.                  ALTER DATABASE [?] SET RECOVERY SIMPLE
    7.                  dbcc ShrinkFile(['' + @LogFile + ''])
    8.                  ALTER DATABASE [?] SET RECOVERY FULL''
    9.  
    10.            use [?]
    11.            If Exists(Select * From [?].Information_Schema.Routines Where Specific_Name = ''ShrinkMe'')
    12.                  Begin
    13.                        drop Procedure ShrinkMe
    14.                  End
    15.            use [?]    
    16.            Exec (@LogFile)  
    17.            --print(@LogFile)
    18.            exec [?].dbo.ShrinkMe
    19.      End '

    This link allows to check the recovery model for the database
    http://blog.sqlauthority.com/2009/07/16/sql-server-four-different-ways-to-find-recovery-model-for-database/



    *** Remember, if you have a SQL related question, try our Microsoft SQL Server Programming forum or our Microsoft SQL Server Admin forum

    About the Author

    Naomi Nosonovsky, Senior Software Developer, has more than 15 years of enterprise experience in analysis, design, development and implementation of high-performance client/server data management solutions. She is a Personality of the Year for 2008, 2009, 2010 and 2011 at www.universalthread.com in .NET, SQL Server & VFP categories. She is also an All Star contributor/MCC Community Contributor at forums.asp.net and MSDN T-SQL forum.
    Social SitingsFacebookLinkedIndeliciousLTD RSS Feed
    10642 views
    Instapaper

    7 comments

    Comment from: SQLDenis [Member] Email
    *****
    SQLDenis Can't wait for onpnt's reply :-)

    I understand this is what someone asked for but you should NOT shrink your files

    onpnt explains why here: http://blogs.lessthandot.com/index.php/DataMgmt/DBAdmin/MSSQLServerAdmin/do-not-truncate-your-ldf-files
    07/12/09 @ 04:33
    Comment from: Naomi Nosonovsky [Member]
    Naomi Nosonovsky I believe onpnt said that Shrinkfile is OK. :)

    Thanks for the feedback.



    BTW, I changed the blog's title now.
    07/12/09 @ 06:39
    Comment from: Ted Krueger (onpnt) [Member]
    Ted Krueger (onpnt) shrinkfile still causes fragmentation on disk and countless shrinkfiles can become an issue. It is of course the best course if you have a file growth issue and you need to do something about it. I thought of throwing a party when I saw truncate removed in 2008. ;-) Just last week I had to do a shrinkfile on a log that went out there due to a index maintenance and backup issue that made the log go to around 70GB. It's not really a problem but did cause a problem with my mirror and it was a mistake on my part and should have been avoided.

    I highly recommend reading a few things and concerns on anything that goes with shrink though so understand what you really are doing to things and performance.

    8 Steps to better Transaction Log throughput
    http://www.sqlskills.com/blogs/kimberly/post/8-Steps-to-better-Transaction-Log-throughput.aspx

    Transaction Log VLFs - too many or too few?
    http://www.sqlskills.com/BLOGS/KIMBERLY/post/Transaction-Log-VLFs-too-many-or-too-few.aspx

    07/13/09 @ 19:15
    Comment from: Ted Krueger (onpnt) [Member]
    ****-
    Ted Krueger (onpnt) On the blog, the procedure has some concerns with it. Not really the procedure but the concept of what it is doing is a problem. Setting the recovery to simple really doesn't solve the problem at hand. Log backups and proper log maintenance will. The largest problem that is faced in doing the steps of altering the recovery mode however, is the state you put the db in. Basically you are putting yourself in a critical state that you cannot recover from and could very well lose your db if this goes bad. I would recommend not going in this route and treating every log (single or groups) for each db uniquely and set the growth and size depending on the type and level of transactions coming into the db. given some time you can keep logs well in check and not a problem that you come back from vacations to :-)
    07/13/09 @ 19:21
    Comment from: The Review Site [Visitor] · http://www.google.com
    The Review Site Good post congratulations.
    11/12/09 @ 05:52
    Comment from: Duncan Dubick [Visitor]
    Duncan Dubick I needed a script like this and came across this one. Had to modify it a bit since the first script listed is shrinking file_id 1 (which is the primary data file). I changed this so it is looking at the type field (=1) in master.sys.databases. Also, this script doesn't check to see if the database was in simple recovery model to start with! Changed all of my simple databases to Full. Lastly, the 'tempdb error' is caused by sql validating the generated code for each iteration. Since you can't change the recovery model on tempdb it is spitting out the message. I hid those lines in exec statements.


    sp_MSForEachDb 'IF ''?'' NOT IN (''master'', ''tempdb'', ''tempdev'', ''model'', ''msdb'')
    AND (SELECT recovery_model FROM master.sys.databases WHERE name = ''?'') = 1
    AND (SELECT is_read_only FROM master.sys.databases WHERE name = ''?'') = 0
    BEGIN
    declare @LogFile nvarchar(2000)
    USE [?]
    SELECT @LogFile = sys.database_files.name
    FROM sys.database_files
    WHERE (sys.database_files.type = 1)
    PRINT @LogFile
    EXEC(''ALTER DATABASE [?] SET RECOVERY SIMPLE'')
    DBCC SHRINKFILE (@LogFile, 1)
    EXEC(''ALTER DATABASE [?] SET RECOVERY FULL'')
    END'
    05/05/10 @ 09:33
    Comment from: Naomi Nosonovsky [Member]
    Naomi Nosonovsky Duncan,

    Thanks a lot for your comment - very valid points. I'm also thinking, that using undocumented stored procedure could have been a wrong idea to begin with.

    Take a look here for a similar problem of looping through all databases

    Relevant MSDN thread

    Please take a look at last comment by Adam Haines in the mentioned thread - very important!
    05/05/10 @ 09:45

    Leave a comment


    Your email address will not be revealed on this site.

    Your URL will be displayed.
    (Line breaks become <br />)
    (Name, email & website)
    (Allow users to contact you through a message form (your email will not be revealed.)