Created
June 20, 2025 16:47
-
-
Save nanoDBA/4d60bfa1eef729e64521d3020e4d4165 to your computer and use it in GitHub Desktop.
π Souped-up space reporting stored procedure (originally by Richard Ding) that details your database sizes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
File: sp_SDS.sql | |
Description: π A souped-up space-reporting stored procedure that tells you everything about your database sizes. | |
Purpose: This procedure provides detailed reports on database space usage, from high-level summaries down to | |
individual file details. It's designed for DBAs who need to quickly diagnose space issues, monitor | |
growth, and generate reports for capacity planning. It includes advanced filtering by filegroup, | |
logical file name, and physical path, with flexible output formats for both interactive use and Nagios | |
monitoring. Think of it as sp_spaceused on steroids. πͺ | |
Created: 2008-04-08 | |
Author: Richard Ding | |
Modified: 2025-06-20 by @nanoDBA | |
*/ | |
-- AI-Updated: 2025-06-20 11:32:00 | Marker: AIUPD-20250620113200-73705f5344532e73716c #| | |
-- Summary: | |
-- π§ The Swiss Army knife for checking database space. | |
-- This procedure gives you a deep dive into space allocation, with features like: | |
-- - File-level and database-level views | |
-- - Filtering by filegroup, logical name, and physical path | |
-- - Nagios-friendly output for automated monitoring - π€ | |
-- - Different output modes ('simple' vs. 'normal') for tailored reports | |
-- | |
-- Recommendations: | |
-- - π§ Use @TargetDatabase for a single database to speed things up. | |
-- - π The @Level = 'File' parameter is great for hunting down specific space hogs. | |
-- - β° Be mindful that running this on all databases can take time on servers with many DBs. | |
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_SDS') | |
EXEC ('CREATE PROC dbo.sp_SDS AS SELECT ''stub version, to be replaced''') | |
GO | |
ALTER PROCEDURE [dbo].[sp_SDS] | |
@TargetDatabase sysname = NULL, -- NULL: all dbs, or specify a single one. | |
@Level varchar(10) = 'Database', -- 'Database' for summary view, 'File' for excruciating detail. | |
@UpdateUsage bit = 0, -- Run DBCC UPDATEUSAGE. Use with caution in production. | |
@Unit char(2) = 'GB', -- 'MB', 'KB', or 'GB'. Let's be real, it's usually 'GB'. | |
/* extended vars added to limit/expand what we output -ryanfrantz 20120210 */ | |
@mode varchar(6) = 'normal', -- 'normal' for all columns, 'simple' for a cleaner look. | |
@allocation_stats varchar(5) = 'false', -- [true|false]; if true, only display allocation stats | |
@reserved_stats varchar(5) = 'false', -- [true|false]; if true, only display reserved stats | |
@nagios varchar(5) = 'true', -- [true|false]; Nagios output takes precedence over all other formats. | |
@FileGroupName varchar(30) = NULL, -- Filter by filegroup name. Hallelujah! | |
@LogicalFileName varchar(255) = NULL, -- Filter by logical file name (supports wildcards). | |
@PhysicalPath varchar(255) = NULL -- Filter by physical file path (supports wildcards). | |
AS | |
/************************************************************************************************** | |
** | |
** author: Richard Ding | |
** date: 4/8/2008 | |
** usage: list db size AND path w/o SUMmary | |
** test code: sp_SDS -- default behavior | |
** sp_SDS 'maAster' | |
** sp_SDS NULL, NULL, 0 | |
** sp_SDS NULL, 'file', 1, 'GB' | |
** sp_SDS 'Test_snapshot', 'Database', 1 | |
** sp_SDS 'Test', 'File', 0, 'kb' | |
** sp_SDS 'pfaids', 'Database', 0, 'gb' | |
** sp_SDS 'tempdb', NULL, 1, 'kb' | |
** | |
** | |
** Note from the author I stumbled upon regarding large databases: | |
** "If your database is at TB size, replace dec(10,3) with dev(13,3)." | |
** from http://www.sqlmag.com/forums/aft/96836 | |
** ryanfrantz 20120210 | |
**************************************************************************************************/ | |
SET NOCOUNT ON; | |
-- ================================================================================================= | |
-- SECTION 1: INITIAL VALIDATION AND SETUP | |
-- First, we check if the user-provided database actually exists. No point in continuing if it doesn't. | |
-- ================================================================================================= | |
IF @TargetDatabase IS NOT NULL AND DB_ID(@TargetDatabase) IS NULL | |
BEGIN | |
RAISERROR(15010, -1, -1, @TargetDatabase); | |
RETURN (-1) | |
END | |
/* | |
only call the below code if we're asking about a specific database | |
*/ | |
-- ================================================================================================= | |
-- SECTION 2: SINGLE DATABASE QUICK STATS | |
-- If the user wants stats for just one database, we can take a shortcut for some modes. | |
-- This part is a remnant of the original logic, providing quick allocation/reserved stats. | |
-- ================================================================================================= | |
if @TargetDatabase IS NOT NULL | |
/* lifted from sp_spaceused */ | |
declare @id int -- The object id that takes up space | |
,@type character(2) -- The object type. | |
,@pages bigint -- Working variable for size calc. | |
,@dbname sysname | |
,@dbsize bigint | |
,@logsize bigint | |
,@reservedpages bigint | |
,@usedpages bigint | |
,@rowCount bigint | |
select @dbname = @TargetDatabase | |
begin | |
set @dbname = @TargetDatabase | |
/* we need to make this dynamic SQL to handle calls for specific databases' details */ | |
/* | |
select @dbsize = sum(convert(bigint,case when status & 64 = 0 then size else 0 end)) | |
, @logsize = sum(convert(bigint,case when status & 64 <> 0 then size else 0 end)) | |
from dbo.sysfiles | |
*/ | |
declare @sys_files table ( | |
size int, | |
status int | |
) | |
insert into @sys_files | |
exec(N'SELECT size, status FROM ' + @dbname + '.dbo.sysfiles') | |
select @dbsize = sum(convert(bigint,case when status & 64 = 0 then size else 0 end)) | |
, @logsize = sum(convert(bigint,case when status & 64 <> 0 then size else 0 end)) | |
from @sys_files | |
-- table variable for 'sys.partitions' | |
declare @sys_partitions table ( | |
partition_id bigint, | |
object_id int, | |
index_id int, | |
partition_number int, | |
hobt_id bigint, | |
rows bigint, | |
filestream_filegroup_id smallint, | |
data_compression tinyint, | |
data_compression_desc nvarchar(60) | |
) | |
insert into @sys_partitions | |
exec(N'SELECT * FROM ' + @dbname + '.sys.partitions') | |
-- table variable for 'sys.allocation_units' | |
declare @sys_allocation_units table ( | |
allocation_unit_id bigint, | |
type tinyint, | |
type_desc nvarchar(60), | |
container_id bigint, | |
data_space_id int, | |
total_pages bigint, | |
used_pages bigint, | |
data_pages bigint | |
) | |
insert into @sys_allocation_units | |
exec(N'SELECT * FROM ' + @dbname + '.sys.allocation_units') | |
-- table variable for 'sys.internal_tables' | |
declare @sys_internal_tables table ( | |
name nvarchar(128), | |
object_id int, | |
principal_id int, | |
schema_id int, | |
parent_object_id int, | |
type char(2), | |
type_desc nvarchar(60), | |
create_date datetime, | |
modify_date datetime, | |
is_ms_shipped bit, | |
is_published bit, | |
is_schema_published bit, | |
internal_type tinyint, | |
internal_type_desc nvarchar(60), | |
parent_id int, | |
parent_minor_id int, | |
lob_data_space_id int, | |
filestream_data_space_id int | |
) | |
insert into @sys_internal_tables | |
exec(N'SELECT * FROM ' + @dbname + '.sys.internal_tables') | |
select @reservedpages = sum(a.total_pages), | |
@usedpages = sum(a.used_pages), | |
@pages = sum( | |
CASE | |
-- XML-Index and FT-Index internal tables are not considered "data", but is part of "index_size" | |
When it.internal_type IN (202,204,211,212,213,214,215,216) Then 0 | |
When a.type <> 1 Then a.used_pages | |
When p.index_id < 2 Then a.data_pages | |
Else 0 | |
END | |
) | |
/* we need to make this dynamic SQL to handle calls for specific databases' details */ | |
from @sys_partitions p join @sys_allocation_units a on p.partition_id = a.container_id | |
left join @sys_internal_tables it on p.object_id = it.object_id | |
/* BEGIN @allocation_stats */ | |
/* unallocated space could not be negative */ | |
if @allocation_stats = 'true' | |
begin | |
select | |
--database_name = db_name(), | |
database_name = @dbname, | |
database_size = ltrim(str((convert (dec (15, 2),@dbsize) + convert (dec (15, 2),@logsize)) | |
* 8192 / 1048576.0,15,2) + ' MB'), | |
'unallocated space' = ltrim(str((case when @dbsize >= @reservedpages then | |
(convert (dec (15, 2),@dbsize) - convert (dec (15, 2),@reservedpages)) | |
* 8192 / 1048576.0 else 0 end),15,2) + ' MB') | |
return(0) | |
end | |
/* END @allocation_stats */ | |
/* BEGIN @reserved_stats */ | |
if @reserved_stats = 'true' | |
begin | |
/* | |
** Now calculate the summary data. | |
** reserved: sum(reserved) where indid in (0, 1, 255) | |
** data: sum(data_pages) + sum(text_used) | |
** index: sum(used) where indid in (0, 1, 255) - data | |
** unused: sum(reserved) - sum(used) where indid in (0, 1, 255) | |
*/ | |
select | |
reserved = ltrim(str(convert (dec (15, 2),@reservedpages) * 8192 / 1024.0,15,2) + ' KB'), | |
data = ltrim(str(convert (dec (15, 2),@pages) * 8192 / 1024.0,15,2) + ' KB'), | |
index_size = ltrim(str((convert (dec (15, 2),@usedpages) - convert (dec (15, 2),@pages)) * 8192 / 1024.0,15,2) + ' KB'), | |
unused = ltrim(str((convert (dec (15, 2),@reservedpages) - convert (dec (15, 2),@usedpages)) * 8192 / 1024.0,15,2) + ' KB') | |
return(0) | |
end | |
/* END @reserved_stats */ | |
end | |
-- ================================================================================================= | |
-- SECTION 3: TEMP TABLE INITIALIZATION | |
-- We use global temporary tables (##) to collect data across different database contexts, | |
-- especially when iterating with sp_MSforeachdb. Gotta clean up first to ensure a fresh start. | |
-- It's like wiping the slate clean before starting a masterpiece. π¨ | |
-- ================================================================================================= | |
IF OBJECT_ID('tempdb.dbo.##Tbl_CombinedInfo', 'U') IS NOT NULL | |
DROP TABLE dbo.##Tbl_CombinedInfo; | |
IF OBJECT_ID('tempdb.dbo.##Tbl_DbFileStats', 'U') IS NOT NULL | |
DROP TABLE dbo.##Tbl_DbFileStats; | |
IF OBJECT_ID('tempdb.dbo.##Tbl_ValidDbs', 'U') IS NOT NULL | |
DROP TABLE dbo.##Tbl_ValidDbs; | |
IF OBJECT_ID('tempdb.dbo.##Tbl_Logs', 'U') IS NOT NULL | |
DROP TABLE dbo.##Tbl_Logs; | |
CREATE TABLE dbo.##Tbl_CombinedInfo ( | |
DatabaseName sysname NULL, | |
[type] VARCHAR(30) NULL, -- Increased from VARCHAR(10) to handle full filegroup names. | |
LogicalName sysname NULL, | |
T dec(15, 2) NULL, | |
U dec(15, 2) NULL, | |
[U(%)] dec(5, 2) NULL, | |
F dec(15, 2) NULL, | |
[F(%)] dec(5, 2) NULL, | |
PhysicalName sysname NULL, | |
FileGroup int NULL, | |
TotalExtents bigint NULL, | |
UsedExtents bigint NULL, | |
Name sysname NULL, | |
FileName varchar(255) NULL ); | |
CREATE TABLE dbo.##Tbl_DbFileStats ( | |
Id int identity, | |
DatabaseName sysname NULL, | |
FileId int NULL, | |
FileGroup int NULL, | |
TotalExtents bigint NULL, | |
UsedExtents bigint NULL, | |
Name sysname NULL, | |
FileName varchar(255) NULL ); | |
CREATE TABLE dbo.##Tbl_ValidDbs ( | |
Id int identity, | |
Dbname sysname NULL ); | |
CREATE TABLE dbo.##Tbl_Logs ( | |
DatabaseName sysname NULL, | |
LogSize dec (15, 2) NULL, | |
LogSpaceUsedPercent dec (5, 2) NULL, | |
Status int NULL ); | |
DECLARE @Ver varchar(10), | |
@DatabaseName sysname, | |
@Ident_last int, | |
@String varchar(2000), | |
@BaseString varchar(2000); | |
SELECT @DatabaseName = '', | |
@Ident_last = 0, | |
@String = '', | |
@Ver = CASE WHEN @@VERSION LIKE '%9.0%' THEN 'SQL 2005' | |
WHEN @@VERSION LIKE '%8.0%' THEN 'SQL 2000' | |
WHEN @@VERSION LIKE '%10.0%' THEN 'SQL 2008' | |
WHEN @@VERSION LIKE '%10.5%' THEN 'SQL 2008' | |
WHEN @@VERSION LIKE '%11.0%' THEN 'SQL 2012' | |
WHEN @@VERSION LIKE '%12.0%' THEN 'SQL 2014' | |
WHEN @@VERSION LIKE '%13.0%' THEN 'SQL 2016' | |
WHEN @@VERSION LIKE '%14.0%' THEN 'SQL 2017' | |
WHEN @@VERSION LIKE '%15.0%' THEN 'SQL 2019' | |
END; | |
SELECT @BaseString = | |
' SELECT DB_NAME(), ' + | |
CASE WHEN @Ver = 'SQL 2000' THEN 'CASE WHEN status & 0x40 = 0x40 THEN ''Log'' ELSE ''Data'' END' | |
ELSE ' CASE type WHEN 0 THEN ''Data'' WHEN 1 THEN ''Log'' WHEN 4 THEN ''Full-text'' ELSE ''reserved'' END' END + | |
', name, ' + | |
CASE WHEN @Ver = 'SQL 2000' THEN 'filename' ELSE 'physical_name' END + | |
', cast(size*8.0/1024.0 as dec(15, 2)) FROM ' + | |
CASE WHEN @Ver = 'SQL 2000' THEN 'sysfiles' ELSE 'sys.database_files' END + | |
' WHERE ' | |
+ CASE WHEN @Ver = 'SQL 2000' THEN ' HAS_DBACCESS(DB_NAME()) = 1' ELSE 'state_desc = ''ONLINE''' END + ''; | |
SELECT @String = 'INSERT INTO dbo.##Tbl_ValidDbs SELECT name FROM ' + | |
CASE WHEN @Ver = 'SQL 2000' THEN 'master.dbo.sysdatabases' | |
WHEN @Ver IN ('SQL 2005', 'SQL 2008', 'SQL 2012', 'SQL 2014', 'SQL 2016', 'SQL 2017', 'SQL 2019') THEN 'master.sys.databases' | |
END + ' WHERE HAS_DBACCESS(name) = 1 ORDER BY name ASC'; | |
EXEC (@String); | |
INSERT INTO dbo.##Tbl_Logs EXEC ('DBCC SQLPERF (LOGSPACE) WITH NO_INFOMSGS'); | |
-- For data part | |
IF @TargetDatabase IS NOT NULL | |
BEGIN | |
SELECT @DatabaseName = @TargetDatabase; | |
IF @UpdateUsage <> 0 AND DATABASEPROPERTYEX (@DatabaseName,'Status') = 'ONLINE' | |
AND DATABASEPROPERTYEX (@DatabaseName, 'Updateability') <> 'READ_ONLY' | |
BEGIN | |
SELECT @String = 'USE [' + @DatabaseName + '] DBCC UPDATEUSAGE (0)'; | |
PRINT '*** ' + @String + ' *** '; | |
EXEC (@String); | |
PRINT ''; | |
END | |
SELECT @String = 'INSERT INTO dbo.##Tbl_CombinedInfo (DatabaseName, type, LogicalName, PhysicalName, T) ' + @BaseString; | |
INSERT INTO dbo.##Tbl_DbFileStats (FileId, FileGroup, TotalExtents, UsedExtents, Name, FileName) | |
EXEC ('USE [' + @DatabaseName + '] DBCC SHOWFILESTATS WITH NO_INFOMSGS'); | |
EXEC ('USE [' + @DatabaseName + '] ' + @String); | |
UPDATE dbo.##Tbl_DbFileStats SET DatabaseName = @DatabaseName; | |
END | |
ELSE | |
BEGIN | |
WHILE 1 = 1 | |
BEGIN | |
SELECT TOP 1 @DatabaseName = Dbname FROM dbo.##Tbl_ValidDbs WHERE Dbname > @DatabaseName ORDER BY Dbname ASC; | |
IF @@ROWCOUNT = 0 | |
BREAK; | |
IF @UpdateUsage <> 0 AND DATABASEPROPERTYEX (@DatabaseName, 'Status') = 'ONLINE' | |
AND DATABASEPROPERTYEX (@DatabaseName, 'Updateability') <> 'READ_ONLY' | |
BEGIN | |
SELECT @String = 'DBCC UPDATEUSAGE (''' + @DatabaseName + ''') '; | |
PRINT '*** ' + @String + '*** '; | |
EXEC (@String); | |
PRINT ''; | |
END | |
SELECT @Ident_last = ISNULL(MAX(Id), 0) FROM dbo.##Tbl_DbFileStats; | |
SELECT @String = 'INSERT INTO dbo.##Tbl_CombinedInfo (DatabaseName, type, LogicalName, PhysicalName, T) ' + @BaseString; | |
EXEC ('USE [' + @DatabaseName + '] ' + @String); | |
INSERT INTO dbo.##Tbl_DbFileStats (FileId, FileGroup, TotalExtents, UsedExtents, Name, FileName) | |
EXEC ('USE [' + @DatabaseName + '] DBCC SHOWFILESTATS WITH NO_INFOMSGS'); | |
UPDATE dbo.##Tbl_DbFileStats SET DatabaseName = @DatabaseName WHERE Id BETWEEN @Ident_last + 1 AND @@IDENTITY; | |
END | |
END | |
-- set used size for data files, do not change total obtained from sys.database_files as it has for log files | |
UPDATE dbo.##Tbl_CombinedInfo | |
SET U = cast(s.UsedExtents*8*8/1024.0 as dec(15, 2)) | |
FROM dbo.##Tbl_CombinedInfo t JOIN dbo.##Tbl_DbFileStats s | |
ON t.LogicalName = s.Name AND s.DatabaseName = t.DatabaseName; | |
-- set used size and % values for log files: | |
UPDATE dbo.##Tbl_CombinedInfo | |
SET [U(%)] = LogSpaceUsedPercent, | |
U = cast(T * LogSpaceUsedPercent/100.0 as dec(15, 2)) | |
FROM dbo.##Tbl_CombinedInfo t JOIN dbo.##Tbl_Logs l | |
ON l.DatabaseName = t.DatabaseName | |
WHERE t.type = 'Log'; | |
UPDATE dbo.##Tbl_CombinedInfo SET F = cast(T - U as dec(15, 2)), [U(%)] = cast(U*100.0/T as dec(5, 2)); | |
UPDATE dbo.##Tbl_CombinedInfo SET [F(%)] = cast(F*100.0/T as dec(5, 2)); | |
-- ================================================================================================= | |
-- SECTION 5: FILEGROUP INFORMATION ENRICHMENT | |
-- The 'type' column initially just holds 'Data' or 'Log'. We now enrich it with actual | |
-- filegroup names. This is crucial for the @FileGroupName filter to work correctly. | |
-- We use another sp_MSforeachdb loop to query sys.filegroups in each database context. | |
-- ================================================================================================= | |
-- Create a temporary table to hold filegroup names | |
IF OBJECT_ID('tempdb..#FileGroups') IS NOT NULL | |
DROP TABLE #FileGroups; | |
CREATE TABLE #FileGroups ( | |
DatabaseName sysname, | |
FileGroupId int, | |
FileGroupName sysname | |
); | |
-- For each database, get its filegroup names and file mappings | |
DECLARE @SqlCmd nvarchar(max); | |
SET @SqlCmd = ' | |
USE [?]; | |
IF DB_ID() IS NOT NULL | |
BEGIN | |
INSERT INTO #FileGroups (DatabaseName, FileGroupId, FileGroupName) | |
SELECT DB_NAME(), fg.data_space_id, fg.name | |
FROM sys.filegroups fg; | |
-- Update CombinedInfo with FileGroup information | |
-- This is where the magic happens: we replace ''Data'' with the actual filegroup name. | |
UPDATE c | |
SET c.type = CASE | |
WHEN df.type = 1 THEN ''Log'' | |
ELSE COALESCE(fg.name, ''PRIMARY'') -- If a file is not in a specific filegroup, it is in PRIMARY. | |
END | |
FROM dbo.##Tbl_CombinedInfo c | |
INNER JOIN [?].sys.database_files df ON df.name = c.LogicalName | |
LEFT JOIN [?].sys.filegroups fg ON df.data_space_id = fg.data_space_id | |
WHERE c.DatabaseName = DB_NAME(); | |
END | |
'; | |
EXEC sp_MSforeachdb @SqlCmd; | |
-- ================================================================================================= | |
-- SECTION 6: FILE-LEVEL OUTPUT | |
-- If the user asked for @Level = 'File', this is where we generate the output. | |
-- We handle different formatting based on @nagios, @mode, and specific file filters. | |
-- The precedence is: Nagios > Specific Filters > General Output. | |
-- ================================================================================================= | |
IF UPPER(ISNULL(@Level, 'DATABASE')) = 'FILE' | |
BEGIN | |
-- First, convert units if needed. | |
IF @Unit = 'KB' | |
UPDATE dbo.##Tbl_CombinedInfo | |
SET T = T * 1024, U = U * 1024, F = F * 1024; | |
IF @Unit = 'GB' | |
UPDATE dbo.##Tbl_CombinedInfo | |
SET T = T / 1024, U = U / 1024, F = F / 1024; | |
-- Nagios format takes precedence over all other parameters. It returns a single, clean result set. | |
IF @nagios = 'true' | |
BEGIN | |
SELECT | |
DatabaseName AS 'DATABASE', | |
type AS 'TYPE', | |
LogicalName AS 'FILE', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(T, 2)), 8) AS 'TOTAL', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(U, 2)), 8) AS 'USED', | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([U(%)], 2)) + '%', 7) AS 'USED %', | |
CASE WHEN @Unit = 'GB' THEN 'GB' WHEN @Unit = 'KB' THEN 'KB' ELSE 'MB' END AS 'UNIT' | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = ISNULL(@TargetDatabase, c.DatabaseName) | |
AND (@FileGroupName IS NULL OR type = @FileGroupName) | |
AND (@LogicalFileName IS NULL OR LogicalName LIKE @LogicalFileName) | |
AND (@PhysicalPath IS NULL OR PhysicalName LIKE @PhysicalPath) | |
ORDER BY DatabaseName ASC, type ASC; | |
RETURN; -- We're done here if it's for Nagios. No more output. | |
END | |
-- For specific file or filegroup queries, return only the matching files without the summary. | |
-- This avoids clutter when the user is asking a very specific question. | |
IF @FileGroupName IS NOT NULL OR @LogicalFileName IS NOT NULL OR @PhysicalPath IS NOT NULL | |
BEGIN | |
IF @Mode = 'simple' AND @PhysicalPath IS NULL | |
SELECT DatabaseName AS 'Database', | |
type AS 'Type', | |
LogicalName, | |
T AS 'Total', | |
U AS 'Used', | |
[U(%)] AS 'Used %' | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = ISNULL(@TargetDatabase, c.DatabaseName) | |
AND (@FileGroupName IS NULL OR type = @FileGroupName) | |
AND (@LogicalFileName IS NULL OR LogicalName LIKE @LogicalFileName) | |
AND (@PhysicalPath IS NULL OR PhysicalName LIKE @PhysicalPath) | |
ORDER BY DatabaseName ASC, type ASC; | |
ELSE | |
SELECT DatabaseName AS 'Database', | |
type AS 'Type', | |
LogicalName, | |
T AS 'Total', | |
U AS 'Used', | |
[U(%)] AS 'Used (%)', | |
F AS 'Free', | |
[F(%)] AS 'Free (%)', | |
CASE | |
WHEN @PhysicalPath IS NOT NULL THEN PhysicalName | |
WHEN @Mode != 'simple' THEN PhysicalName | |
ELSE NULL | |
END AS PhysicalName | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = ISNULL(@TargetDatabase, c.DatabaseName) | |
AND (@FileGroupName IS NULL OR type = @FileGroupName) | |
AND (@LogicalFileName IS NULL OR LogicalName LIKE @LogicalFileName) | |
AND (@PhysicalPath IS NULL OR PhysicalName LIKE @PhysicalPath) | |
ORDER BY DatabaseName ASC, type ASC; | |
RETURN; -- Again, we're done. The user got their specific answer. | |
END | |
-- If no specific file filters, show all files with a summary row. This is the default 'File' level behavior. | |
IF @Mode = 'simple' | |
BEGIN | |
SELECT DatabaseName AS 'Database', | |
type AS 'Type', | |
LogicalName, | |
T AS 'Total', | |
U AS 'Used', | |
[U(%)] AS 'Used %' | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = ISNULL(@TargetDatabase, c.DatabaseName) | |
ORDER BY DatabaseName ASC, type ASC; | |
SELECT CASE WHEN @Unit = 'GB' THEN 'GB' WHEN @Unit = 'KB' THEN 'KB' ELSE 'MB' END AS 'SUM', | |
SUM(T) AS 'TOTAL', SUM(U) AS 'USED', SUM(F) AS 'FREE' | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = ISNULL(@TargetDatabase, c.DatabaseName); | |
END | |
ELSE | |
BEGIN | |
SELECT DatabaseName AS 'Database', | |
type AS 'Type', | |
LogicalName, | |
T AS 'Total', | |
U AS 'Used', | |
[U(%)] AS 'Used (%)', | |
F AS 'Free', | |
[F(%)] AS 'Free (%)', | |
PhysicalName | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = ISNULL(@TargetDatabase, c.DatabaseName) | |
ORDER BY DatabaseName ASC, type ASC; | |
SELECT CASE WHEN @Unit = 'GB' THEN 'GB' WHEN @Unit = 'KB' THEN 'KB' ELSE 'MB' END AS 'SUM', | |
SUM(T) AS 'TOTAL', SUM(U) AS 'USED', SUM(F) AS 'FREE' | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = ISNULL(@TargetDatabase, c.DatabaseName); | |
END | |
END | |
-- ================================================================================================= | |
-- SECTION 7: DATABASE-LEVEL OUTPUT | |
-- This section aggregates all the file-level data into a database-level summary. | |
-- It uses a temporary table #Tbl_Final to stage the aggregated results before display. | |
-- ================================================================================================= | |
IF UPPER(ISNULL(@Level, 'DATABASE')) = 'DATABASE' | |
BEGIN | |
-- Create #Tbl_Final if it doesn't exist. This table holds our aggregated data. | |
IF OBJECT_ID('tempdb..#Tbl_Final') IS NOT NULL | |
DROP TABLE #Tbl_Final; | |
CREATE TABLE #Tbl_Final ( | |
DatabaseName sysname, | |
TOTAL dec(15, 2), | |
[=] char(1), | |
used dec(15, 2), | |
[used (%)] dec(5, 2), | |
[+] char(1), | |
free dec(15, 2), | |
[free (%)] dec(5, 2), | |
[==] char(2), | |
Data dec(15, 2), | |
Data_Used dec(15, 2), | |
[Data_Used (%)] dec(5, 2), | |
Data_Free dec(15, 2), | |
[Data_Free (%)] dec(5, 2), | |
[++] char(2), | |
Log dec(15, 2), | |
Log_Used dec(15, 2), | |
[Log_Used (%)] dec(5, 2), | |
Log_Free dec(15, 2), | |
[Log_Free (%)] dec(5, 2) | |
); | |
-- This is where we pivot the data, summing up Data and Log files separately for each database. | |
INSERT INTO #Tbl_Final | |
SELECT x.DatabaseName, | |
cast(x.Data + y.Log as dec(15, 2)) AS 'TOTAL', | |
'=' AS '=', | |
cast(x.Data_Used + y.Log_Used as dec(15, 2)) AS 'used', | |
cast((x.Data_Used + y.Log_Used)*100.0 / (x.Data + y.Log) as dec(5, 2)) AS 'used (%)', | |
'+' AS '+', | |
cast(x.Data_Free + y.Log_Free as dec(15, 2)) AS 'free', | |
cast((x.Data_Free + y.Log_Free)*100.0 / (x.Data + y.Log) as dec(5, 2)) AS 'free (%)', | |
'==' AS '==', | |
cast(x.Data as dec(15, 2)), | |
cast(x.Data_Used as dec(15, 2)), | |
cast(x.Data_Used*100/x.Data as dec(5, 2)) AS 'Data_Used (%)', | |
cast(x.Data_Free as dec(15, 2)), | |
cast(x.Data_Free*100/x.Data as dec(5, 2)) AS 'Data_Free (%)', | |
'++' AS '++', | |
cast(y.Log as dec(15, 2)), | |
cast(y.Log_Used as dec(15, 2)), | |
cast(y.Log_Used*100/y.Log as dec(5, 2)) AS 'Log_Used (%)', | |
cast(y.Log_Free as dec(15, 2)), | |
cast(y.Log_Free*100/y.Log as dec(5, 2)) AS 'Log_Free (%)' | |
FROM ( | |
SELECT d.DatabaseName, | |
SUM(d.T) AS 'Data', | |
SUM(d.U) AS 'Data_Used', | |
SUM(d.F) AS 'Data_Free' | |
FROM dbo.##Tbl_CombinedInfo d | |
WHERE d.type != 'Log' | |
AND (@FileGroupName IS NULL OR d.type = @FileGroupName) | |
AND (@LogicalFileName IS NULL OR d.LogicalName LIKE @LogicalFileName) | |
AND (@PhysicalPath IS NULL OR d.PhysicalName LIKE @PhysicalPath) | |
GROUP BY d.DatabaseName | |
) AS x | |
JOIN ( | |
SELECT l.DatabaseName, | |
SUM(l.T) AS 'Log', | |
SUM(l.U) AS 'Log_Used', | |
SUM(l.F) AS 'Log_Free' | |
FROM dbo.##Tbl_CombinedInfo l | |
WHERE l.type = 'Log' | |
GROUP BY l.DatabaseName | |
) AS y ON x.DatabaseName = y.DatabaseName; | |
-- Convert units for the final output table. | |
IF @Unit = 'KB' | |
UPDATE #Tbl_Final SET TOTAL = TOTAL * 1024, | |
used = used * 1024, | |
free = free * 1024, | |
Data = Data * 1024, | |
Data_Used = Data_Used * 1024, | |
Data_Free = Data_Free * 1024, | |
Log = Log * 1024, | |
Log_Used = Log_Used * 1024, | |
Log_Free = Log_Free * 1024; | |
IF @Unit = 'GB' | |
UPDATE #Tbl_Final SET TOTAL = TOTAL / 1024, | |
used = used / 1024, | |
free = free / 1024, | |
Data = Data / 1024, | |
Data_Used = Data_Used / 1024, | |
Data_Free = Data_Free / 1024, | |
Log = Log / 1024, | |
Log_Used = Log_Used / 1024, | |
Log_Free = Log_Free / 1024; | |
DECLARE @GrantTotal dec(15, 2); | |
SELECT @GrantTotal = SUM(TOTAL) FROM #Tbl_Final; | |
-- Finally, display the results in the requested format (simple, normal, or nagios). | |
IF @Mode = 'simple' | |
IF @nagios = 'true' | |
SELECT | |
DatabaseName AS 'DATABASE', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(TOTAL, 2)), 8) AS 'DATABASE TOTAL', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data, 2)), 8) AS 'DATA TOTAL', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data_Used, 2)), 8) AS 'DATA USED', | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Data_Used (%)], 2)) + '%', 7) AS 'DATA USED (%)', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log, 2)), 8) AS 'LOG TOTAL', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log_Used, 2)), 8) AS 'LOG USED', | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Log_Used (%)], 2)) + '%', 7) AS 'LOG USED (%)' | |
FROM #Tbl_Final f | |
WHERE f.DatabaseName LIKE ISNULL(@TargetDatabase, '%') | |
AND (@FileGroupName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_DbFileStats s | |
INNER JOIN #FileGroups fg ON s.DatabaseName = fg.DatabaseName AND s.FileGroup = fg.FileGroupId | |
WHERE s.DatabaseName = f.DatabaseName | |
AND fg.FileGroupName = @FileGroupName | |
)) | |
AND (@LogicalFileName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.LogicalName LIKE @LogicalFileName | |
)) | |
AND (@PhysicalPath IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.PhysicalName LIKE @PhysicalPath | |
)) | |
ORDER BY DatabaseName ASC; | |
ELSE | |
SELECT | |
DatabaseName AS 'DATABASE', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(TOTAL, 2)), 8) AS 'DATABASE TOTAL', | |
[=], | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data, 2)), 8) + ' (' + RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data_Used, 2)), 8) + ', ' + | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Data_Used (%)], 2)) + '%', 7) + ')' AS 'DATA (used, %)', | |
[+], | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log, 2)), 8) + ' (' + RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log_Used, 2)), 8) + ', ' + | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Log_Used (%)], 2)) + '%', 7) + ')' AS 'LOG (used, %)' | |
FROM #Tbl_Final f | |
WHERE f.DatabaseName LIKE ISNULL(@TargetDatabase, '%') | |
AND (@FileGroupName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_DbFileStats s | |
INNER JOIN #FileGroups fg ON s.DatabaseName = fg.DatabaseName AND s.FileGroup = fg.FileGroupId | |
WHERE s.DatabaseName = f.DatabaseName | |
AND fg.FileGroupName = @FileGroupName | |
)) | |
AND (@LogicalFileName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.LogicalName LIKE @LogicalFileName | |
)) | |
AND (@PhysicalPath IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.PhysicalName LIKE @PhysicalPath | |
)) | |
ORDER BY DatabaseName ASC; | |
ELSE | |
IF @nagios = 'true' | |
SELECT | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND(cast(TOTAL*100.0/@GrantTotal as dec(15, 2)), 2)) + '%', 7) AS 'WEIGHT (%)', | |
DatabaseName AS 'DATABASE', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(used, 2)), 8) AS 'DATABASE USED', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(free, 2)), 8) AS 'DATABASE FREE', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(TOTAL, 2)), 8) AS 'DATABASE TOTAL', | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([used (%)], 2)) + '%', 7) AS 'DATABASE USED (%)', | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([free (%)], 2)) + '%', 7) AS 'DATABASE FREE (%)', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data, 2)), 8) AS 'DATA TOTAL', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data_Used, 2)), 8) AS 'DATA USED', | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Data_Used (%)], 2)) + '%', 7) AS 'DATA USED (%)', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log, 2)), 8) AS 'LOG TOTAL', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log_Used, 2)), 8) AS 'LOG USED', | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Log_Used (%)], 2)) + '%', 7) AS 'LOG USED (%)' | |
FROM #Tbl_Final f | |
WHERE f.DatabaseName LIKE ISNULL(@TargetDatabase, '%') | |
AND (@FileGroupName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_DbFileStats s | |
INNER JOIN #FileGroups fg ON s.DatabaseName = fg.DatabaseName AND s.FileGroup = fg.FileGroupId | |
WHERE s.DatabaseName = f.DatabaseName | |
AND fg.FileGroupName = @FileGroupName | |
)) | |
AND (@LogicalFileName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.LogicalName LIKE @LogicalFileName | |
)) | |
AND (@PhysicalPath IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.PhysicalName LIKE @PhysicalPath | |
)) | |
ORDER BY DatabaseName ASC; | |
ELSE | |
SELECT | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND(cast(TOTAL*100.0/@GrantTotal as dec(15, 2)), 2)) + '%', 7) AS 'WEIGHT (%)', | |
DatabaseName AS 'DATABASE', | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(used, 2)), 8) + ' (' + RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([used (%)], 2)) + '%', 7) + ')' AS 'DATABASE USED (%)', | |
[+], | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(free, 2)), 8) + ' (' + RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([free (%)], 2)) + '%', 7) + ')' AS 'DATABASE FREE (%)', | |
[=], | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(TOTAL, 2)), 8) AS 'DATABASE TOTAL', | |
[=], | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data, 2)), 8) + ' (' + RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Data_Used, 2)), 8) + ', ' + | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Data_Used (%)], 2)) + '%', 7) + ')' AS 'DATA (used, %)', | |
[+], | |
RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log, 2)), 8) + ' (' + RIGHT(SPACE(8) + CONVERT(VARCHAR(8), ROUND(Log_Used, 2)), 8) + ', ' + | |
RIGHT(SPACE(7) + CONVERT(VARCHAR(6), ROUND([Log_Used (%)], 2)) + '%', 7) + ')' AS 'LOG (used, %)' | |
FROM #Tbl_Final f | |
WHERE f.DatabaseName LIKE ISNULL(@TargetDatabase, '%') | |
AND (@FileGroupName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_DbFileStats s | |
INNER JOIN #FileGroups fg ON s.DatabaseName = fg.DatabaseName AND s.FileGroup = fg.FileGroupId | |
WHERE s.DatabaseName = f.DatabaseName | |
AND fg.FileGroupName = @FileGroupName | |
)) | |
AND (@LogicalFileName IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.LogicalName LIKE @LogicalFileName | |
)) | |
AND (@PhysicalPath IS NULL OR EXISTS ( | |
SELECT 1 | |
FROM dbo.##Tbl_CombinedInfo c | |
WHERE c.DatabaseName = f.DatabaseName | |
AND c.PhysicalName LIKE @PhysicalPath | |
)) | |
ORDER BY DatabaseName ASC; | |
IF @TargetDatabase IS NULL | |
SELECT CASE WHEN @Unit = 'GB' THEN 'GB' WHEN @Unit = 'KB' THEN 'KB' ELSE 'MB' END AS 'SUM', | |
SUM (used) AS 'USED', | |
SUM (free) AS 'FREE', | |
SUM (TOTAL) AS 'TOTAL', | |
SUM (Data) AS 'DATA', | |
SUM (Log) AS 'LOG' | |
FROM #Tbl_Final; | |
END | |
RETURN (0) | |
GO |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment