SQL Server:如何从sys.master_files中获取正确的数据库大小?

时间:2019-04-03 17:26:23

标签: sql-server group-by type-conversion aggregate

我正在处理一个查询,该查询收集了一些与数据库还原有关的信息,但是在获取正确的数据库大小时遇到​​了麻烦。以下查询为我提供了数据库名称,上次还原日期,数据库大小以及最后一次还原数据库的用户的用户名。

WITH lastrestores AS
(
    SELECT 
        DatabaseName = [d].[name], 
        [r].[restore_date], 
        [f].[size], 
        [r].[user_name], 
        RowNum = ROW_NUMBER() OVER (PARTITION BY d.NAME ORDER BY r.[restore_date] DESC) 
    FROM   
        master.sys.databases d 
    LEFT OUTER JOIN 
        msdb.dbo.[restorehistory] r ON r.[destination_database_name] = d.NAME 
    LEFT JOIN 
        master.sys.master_files f ON d.database_id = f.database_id
) 
SELECT * 
FROM [lastrestores] 
WHERE [rownum] = 1 
  AND databasename LIKE 'stuff%' 
ORDER BY restore_date DESC

但是,这没有显示正确的数据库大小。当我检查.mdf文件和数据库属性中的大小时,它显示的大小小于此查询返回的大小。当我检查sp_databases存储过程时,它表明数据库大小已完成:

DATABASE_SIZE = CONVERT(INT,
                        CASE   -- more than 2TB(maxint) worth of pages (by 8K each) can not fit an int...
                           WHEN SUM(CONVERT(BIGINT, s_mf.size)) >= 268435456
                              THEN NULL
                              ELSE SUM(CONVERT(BIGINT, s_mf.size)) * 8 -- Convert from 8192 byte pages to Kb
                        END)

我尝试将此部分合并到原始查询中,但是遇到“未包含在聚合函数或分组依据中”错误:

WITH lastrestores AS
(
    SELECT 
        DatabaseName = [d].[name], 
        [r].[restore_date], 
        CONVERT(INT,
                CASE
                   WHEN SUM(CONVERT(BIGINT, [f].[size])) >= 268435456
                      THEN NULL
                      ELSE SUM(CONVERT(BIGINT, [f].[size])) * 8
                END) AS DBSize, 
                [r].[user_name], 
        RowNum = ROW_NUMBER() OVER (PARTITION BY d.NAME ORDER BY r.[restore_date] DESC) 
    FROM   
        master.sys.databases d 
    LEFT OUTER JOIN 
        msdb.dbo.[restorehistory] r ON r.[destination_database_name] = d.NAME 
    LEFT JOIN 
        master.sys.master_files f ON d.database_id = f.database_id
) 
SELECT * 
FROM [lastrestores] 
WHERE [rownum] = 1 
  AND databasename LIKE 'stuff%' 
ORDER BY restore_date DESC 

虽然我确实了解此错误的基本知识,但我不确定如何调整此错误,因此我可以得到所需的信息,因为此查询比以往更加复杂。我的理想结果是我发布的原始查询,但具有正确的数据库大小,如sp_databases中所示。我该如何实现?

1 个答案:

答案 0 :(得分:2)

如果您只是想转换大小列以匹配属性中的值,则只需将页面转换为MB-因此乘以8即可得到KB,然后除以1024即可得到MB。

WITH lastrestores AS
(
    SELECT 
        DatabaseName = [d].[name], 
        [r].[restore_date], 
        [size] = CAST([f].[size] * 8 / 1024.0 AS DECIMAL(10,2)) , 
        [r].[user_name], 
        RowNum = ROW_NUMBER() OVER (PARTITION BY d.NAME ORDER BY r.[restore_date] DESC) 
    FROM   
        master.sys.databases d 
    LEFT OUTER JOIN 
        msdb.dbo.[restorehistory] r ON r.[destination_database_name] = d.NAME 
    LEFT JOIN 
        master.sys.master_files f ON d.database_id = f.database_id
) 
SELECT * 
FROM [lastrestores] 
WHERE [rownum] = 1 
ORDER BY restore_date DESC

由于SSMS向上取整,因此此Size值可能比在属性中看到的值更准确。

我担心的是,如果您有多个数据文件,这将无法准确显示数据库的非日志大小。

我可能会做类似的事情来包含所有数据文件。

SELECT 
    DatabaseName = DB_NAME(f.database_id)
    ,r.restore_date
    ,CAST(SUM(f.size * 8 / 1024.0) AS DECIMAL(10,2))
    ,r.user_name
FROM sys.master_files f
OUTER APPLY 
    (SELECT TOP 1 * FROM msdb.dbo.[restorehistory] r WHERE r.[destination_database_name] = DB_NAME(f.database_id) ORDER BY restore_date desc) r 
WHERE f.type = 0
GROUP BY f.database_id, r.restore_date, r.user_name
ORDER BY r.restore_date desc