【问题标题】:How to loop through databases to determine which log file to shrink?如何遍历数据库以确定要收缩的日志文件?
【发布时间】:2020-07-14 21:50:47
【问题描述】:

在我的 SQL 实例中,我有大约 50 个处于简单恢复模式的数据库。有时会有很多活动导致日志文件膨胀并填满日志驱动器。

我尝试编写一个脚本每天执行一次,以检查哪些日志文件大于 30 GB,并且有 80% 或更多的可用空间。只有这样我才想运行 DBCC Shrinkfile。 但是,我现在不知道如何在脚本中放置一个循环来循环浏览数据库并捕获 名称和日志文件名。

-- create result holder

DECLARE @result TABLE
    (
      [Database_Name] VARCHAR(150) ,
      [Log_Size] FLOAT ,
      [Log_Space] FLOAT ,
      [Status] VARCHAR(100)
    ) 
 
INSERT  INTO @result
        EXEC ( 'DBCC sqlperf(LOGSPACE) WITH NO_INFOMSGS'
            )
 
-- only return for the DB in context, rounding it 

Declare @SQL varchar(2000), @logsize float,@logspace float

SELECT  @logsize =Round(Log_Size,2), @logspace=100 - Log_space
FROM @result



If @logsize >= 30000 and @logspace >= 80  
set @SQL = 'use ' +'[NameofDatabase]'+' dbcc shrinkfile ([Enternameoflogfile], 20000, TRUNCATEONLY) WITH NO_INFOMSGS'

exec (@SQL)

【问题讨论】:

    标签: sql-server


    【解决方案1】:

    您可以使用CURSOR 来做到这一点,但这种方法或任何类似方法存在一个问题。当另一个仍在运行时,您无法启动新的DBCC SHRINKFILE 操作。而且我想不出一种可靠的方法来让循环“等待”直到前一个操作完成。您可以使用WAITFOR DELAY 并尝试不同的时间间隔,但它仍然不一定保证语句已完成。这是我修改你的代码的方法,延迟了 10 秒。

    SET NOCOUNT ON
    
    DECLARE @log_data TABLE
        (
          [Database_Name] sysname,
          [Log_Size] float,
          [Log_Space] float,
          [Status] varchar(100)
        );
     
    INSERT INTO @log_data 
    EXEC ('DBCC sqlperf(LOGSPACE) WITH NO_INFOMSGS');
    
    DECLARE @result TABLE 
        (
          [Database_Name] sysname,
          [Log_Size] float,
          [Log_Free_Space] float,
          [Log_Name] sysname
        );
    
    -- Get the log file names
    INSERT INTO @result SELECT d.[name], l.[Log_Size], 100.0 - l.[Log_Space] AS [Log_Free_Space], f.[name]
    FROM sys.databases d JOIN sys.master_files f ON d.database_id=f.database_id JOIN @log_data l ON d.[name] = l.[Database_Name]
    WHERE d.database_id > 4 AND f.[type] = 1; -- id > 4 = no system dbs, type 1 = log file
    
    DECLARE @db_name sysname;
    DECLARE @log_file sysname;
    DECLARE @sql nvarchar(max);
    DECLARE db_cursor CURSOR FOR 
    SELECT [Database_Name], [Log_Name] FROM @result WHERE [Log_Size] >= 30000.0 AND [Log_Free_Space] >= 80.0
    OPEN db_cursor
    FETCH NEXT FROM db_cursor INTO @db_name, @log_file
    WHILE @@FETCH_STATUS = 0
      BEGIN
        SET @sql = N'USE [' + @db_name + N']; DBCC SHRINKFILE ([' + @log_file + N'], 20000, TRUNCATEONLY) WITH NO_INFOMSGS;'
        EXEC (@sql)
        WAITFOR DELAY '00:00:10'; -- experiment with this value to get the correct delay
    
        FETCH NEXT FROM db_cursor INTO @db_name, @log_file -- get the next db_name and log file
      END
    CLOSE db_cursor
    DEALLOCATE db_cursor
    

    【讨论】:

    • 非常感谢!这个脚本就像一个魅力!非常感谢您的帮助。
    • @veenaranganath 欢迎您。请把我的答案标记为正确的!
    【解决方案2】:

    对于数据库循环,您可以使用 sp_MSForEachDB

    对于日志文件的大小,您可以使用以下查询:

    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    SELECT
         [Database Name] = DB_NAME()
         ,[File Name] = F.[name]
         ,[Physical Name] = F.physical_name  
         ,[Total Size In MB] = CAST((F.size / 128.0) as decimal(15, 2))  
         ,[Available Space In MB] = CAST(F.size / 128.0 - CAST(FILEPROPERTY(F.[name], 'SpaceUsed') as int) / 128.0 as decimal(15, 2))  
         ,[Growth In MB] = CAST((F.Growth / 128.0) as decimal(15, 2))
         ,[Filegroup Name] = FG.[name] 
     FROM 
         sys.database_files F
         LEFT OUTER JOIN sys.data_spaces AS FG ON FG.data_space_id = F.data_space_id
     WHERE
         F.data_space_id = 0 —Log File
     ORDER BY
       F.[name] 
     ASC
    

    【讨论】:

      猜你喜欢
      • 2011-01-12
      • 2020-08-16
      • 1970-01-01
      • 2022-12-15
      • 2017-10-31
      • 1970-01-01
      • 2020-04-13
      • 2015-11-14
      • 2015-07-13
      相关资源
      最近更新 更多