SQL Server -- Get Disk Space Usage

Keywords: SQL Server SQL Stored Procedure Database MySQL

For DBA, monitoring disk usage is a necessary task. There is no easy way to obtain disk space usage information. Here's a summary of the scripts saved over the years:

The most commonly used way to view disk free space, which is a must-remember entry to DBA:

-- View Disk Available Space
EXEC master.dbo.xp_fixeddrives

The xp_fixeddrives mode is a bit self-contained and can be used directly. The disadvantage is that you can't view the total size of the disk and the unused disk information of SQL Server.

==============================================================

Using the sys.dm_os_volume_stats function

--======================================================================
--View disk space usage for database files
WITH T1 AS (
SELECT DISTINCT
REPLACE(vs.volume_mount_point,':\','') AS Drive_Name ,
CAST(vs.total_bytes / 1024.0 / 1024 / 1024 AS NUMERIC(18,2)) AS Total_Space_GB ,
CAST(vs.available_bytes / 1024.0 / 1024 / 1024  AS NUMERIC(18,2)) AS Free_Space_GB
FROM    sys.master_files AS f
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.file_id) AS vs
)
SELECT
Drive_Name,
Total_Space_GB,
Total_Space_GB-Free_Space_GB AS Used_Space_GB,
Free_Space_GB,
CAST(Free_Space_GB*100/Total_Space_GB AS NUMERIC(18,2)) AS Free_Space_Percent
FROM T1

Query effect:

The sys.dm_os_volume_stats function is very useful. It can query the total space and free space directly. Unfortunately, only SQL Server 2008 R2 SP1, or higher version, is supported. In addition, the unused disk of database file can not be found.

==============================================================

In order to be compatible with the low version, it can be obtained by xp_fixeddrives+xp_cmdshell. I have written several stored procedures to obtain disk information:

USE [monitor]
GO

/****** Object:  StoredProcedure [dbo].[usp_get_disk_free_size]    Script Date: 2016/5/25 18:21:11 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


-- =============================================
-- Author:        GGA
-- Create date:    2016-2-1
-- Description:    Gathering Remaining Space Information on Disks
-- =============================================
CREATE PROCEDURE [dbo].[usp_get_disk_free_size]
AS
BEGIN
    SET NOCOUNT ON;
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

--==========================================
--Create related tables

IF OBJECT_ID('server_disk_usage') IS NULL
BEGIN
    CREATE TABLE [dbo].[server_disk_usage](
        [disk_num] [nvarchar](10) NOT NULL,
        [total_size_mb] [bigint] NOT NULL CONSTRAINT [DF_server_disk_usage_total_size_mb]  DEFAULT ((0)),
        [free_siez_mb] [bigint] NOT NULL CONSTRAINT [DF_server_disk_usage_free_siez_mb]  DEFAULT ((0)),
        [disk_info] [nvarchar](400) NOT NULL CONSTRAINT [DF_server_disk_usage_disk_info]  DEFAULT (''),
        [check_time] [datetime] NOT NULL CONSTRAINT [DF_server_disk_usage_check_time]  DEFAULT (getdate()),
         CONSTRAINT [PK_server_disk_usage] PRIMARY KEY CLUSTERED 
        (
            [disk_num] ASC
        )
    ) ON [PRIMARY]
END


--==========================================
--View the remaining disk space used by all databases
DECLARE @disk TABLE(
        [disk_num] VARCHAR(50),
        [free_siez_mb] INT)
INSERT INTO @disk
EXEC xp_fixeddrives

--Update the remaining space information of the current disk
UPDATE M
SET M.[free_siez_mb]=D.[free_siez_mb]
FROM [dbo].[server_disk_usage] AS M
INNER JOIN @disk AS D
ON M.[disk_num]=D.[disk_num]

--Insert the remaining space information of the new disk
INSERT INTO [dbo].[server_disk_usage]
(
    [disk_num],
    [free_siez_mb]
)
SELECT 
[disk_num],
[free_siez_mb]
FROM @disk AS D
WHERE NOT EXISTS(
    SELECT 1
    FROM [dbo].[server_disk_usage] AS M 
    WHERE M.[disk_num]=D.[disk_num] )

END



GO

/****** Object:  StoredProcedure [dbo].[usp_get_disk_total_size]    Script Date: 2016/5/25 18:21:11 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO



-- =============================================
-- Author:        GGA
-- Create date:    2016-2-1
-- Description:    Collecting total disk space information
-- =============================================
CREATE PROCEDURE [dbo].[usp_get_disk_total_size]
AS
BEGIN
    SET NOCOUNT ON;
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    
IF NOT EXISTS(SELECT * FROM [dbo].[server_disk_usage]
        WHERE [total_size_mb] = 0)
BEGIN
    RETURN;
END

--==========================================
--open CMDShell
EXEC sp_configure 'show advanced options',1;

RECONFIGURE WITH OVERRIDE;

EXEC sp_configure 'xp_cmdshell',1;

RECONFIGURE WITH OVERRIDE

--========================================
--Create temporary tables to store data for each disk character
CREATE TABLE #tempDisks
(
    ID INT IDENTITY(1,1),
    DiskSpace NVARCHAR(200)
)
--============================================
--Put the disks that need to be checked into the temporary table#checkDisks
SELECT 
ROW_NUMBER()OVER(ORDER BY [disk_num]) AS RID,
[disk_num]
INTO #checkDisks
FROM [dbo].[server_disk_usage] 
WHERE [total_size_mb] = 0;

--============================================
--Circulating Temporary Table#Check Disks checks the total number of disks per disk

DECLARE @disk_num NVARCHAR(20)
DECLARE @total_size_mb INT
DECLARE @sql NVARCHAR(200)
DECLARE @max INT
DECLARE @min INT
SELECT @max=MAX(RID),@min=MIN(RID) FROM #checkDisks

WHILE(@min<=@max)
BEGIN
SELECT @disk_num=[disk_num] 
FROM #checkDisks WHERE RID=@min

SET @sql = N'EXEC sys.xp_cmdshell ''fsutil volume diskfree '+@disk_num+':'+''''
PRINT @sql

INSERT INTO #tempDisks
EXEC sys.sp_executesql @sql

SELECT  @total_size_mb=CAST((RIGHT(DiskSpace,LEN(DiskSpace)
    -CHARINDEX(': ',DiskSpace)-1)) AS BIGINT)/1024/1024
FROM #tempDisks WHERE id = 2

SELECT @total_size_mb,@disk_num

UPDATE [dbo].[server_disk_usage]
SET [total_size_mb]=@total_size_mb
WHERE [disk_num]=@disk_num

--SELECT * FROM  #tempDisks

TRUNCATE TABLE #tempDisks

SET @min=@min+1

END


--==========================================
--CMDShell

EXEC sp_configure 'xp_cmdshell',0;

EXEC  sp_configure 'show advanced options',1;

RECONFIGURE WITH OVERRIDE;


END




GO

/****** Object:  StoredProcedure [dbo].[usp_get_disk_usage]    Script Date: 2016/5/25 18:21:11 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:        GGA
-- Create date:    2016-2-1
-- Description:    Collecting total disk space information
-- =============================================
CREATE PROCEDURE [dbo].[usp_get_disk_usage]
AS
BEGIN
    SET NOCOUNT ON;
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    
    EXEC [dbo].[usp_get_disk_free_size]
    EXEC [dbo].[usp_get_disk_total_size]
    
    SELECT 
    [disk_num] AS Drive_Name
    ,CAST([total_size_mb]/1024.0 AS NUMERIC(18,2)) AS Total_Space_GB
    ,CAST(([total_size_mb]-[free_siez_mb])/1024.0 AS NUMERIC(18,2)) AS Used_Space_GB
    ,CAST([free_siez_mb]/1024.0 AS NUMERIC(18,2)) AS Free_Space_GB
    ,CAST([free_siez_mb]*100/[total_size_mb] AS NUMERIC(18,2)) AS Free_Space_Percent
    ,[disk_info]
    ,[check_time]
    FROM [monitor].[dbo].[server_disk_usage]

END
GO


--==================================
--View Disk Space Use
EXEC [dbo].[usp_get_disk_usage]
 

The results show that:

Only the first time to collect disk information or the first time to collect new disk information will xp_cmdshell be invoked to obtain the total size of the disk and minimize the risk of xp_cmdshell opening. It can be used in conjunction with SQL Server Agent Job, periodically call stored procedures to refresh disk information, and monitor programs directly access data tables or Allow disk information for the last refresh.

The disadvantage of this method is that other processes may shut down xp_cmdshell and cause stored procedure execution failure during the period of getting the total disk size after opening xp_cmdshell. Although the probability is low, it still exists after all.

==============================================================

If you want to skip the stored procedure + SQL Server Agent Job mode and call xp_cmdshell directly through the program, when the program is configured with "RECONFIGURE WITH OVERRIDE", the following error will be reported:

CONFIG statement cannot be used inside a user transaction.DB-Lib error message 574

Errors are similar to our use of transaction wrapping sp_configuration statements in SSMS, such as:

BEGIN TRAN
EXEC sp_configure 'show advanced options',1;
RECONFIGURE WITH OVERRIDE;
EXEC sp_configure 'xp_cmdshell',1;
RECONFIGURE WITH OVERRIDE;
COMMIT

The error message is:

The configuration option'show advanced options'has been changed from 0 to 1. Please run the RECONFIGURE statement for installation.
Message 574, level 16, status 0, line 3
CONFIG statements cannot be used in user transactions.
The configuration option'xp_cmdshell'has been changed from 0 to 1. Please run the RECONFIGURE statement for installation.
Message 574, level 16, status 0, line 5
CONFIG statements cannot be used in user transactions.

Can't the RECONFIGURE WITH OVERRIDE statement be invoked programmatically?

Of course not, the relevant errors under google, only found the following correlation, interested can refer to the following:

https://www.sqlservercentral.com/Forums/Topic1349778-146-1.aspx

After a rough look, I used the stored procedure suite stored procedure to bypass the error reporting. I did not have specific tests and felt too cumbersome. So I used a simple and rough way. Since the report "CONFIG statement can not be used in user affairs", how can I get rid of "user affairs" under COMMIT first?

Based on this idea, the final test obtains the following ways:

DECLARE @sql VARCHAR(2000)
SET @sql ='
COMMIT;
EXEC sp_configure ''show advanced options'',1;
RECONFIGURE WITH OVERRIDE;
EXEC sp_configure ''xp_cmdshell'',1;
RECONFIGURE WITH OVERRIDE;
'
EXEC(@sql)

Careful friends found that I first executed COMMIT, you see correctly, although this open way is strange, but it is indeed an open way, the results of execution in SMS are as follows:

Message 3902, level 16, status 1, line 2
COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
The configuration option'show advanced options'has been changed from 1 to 1. Please run the RECONFIGURE statement for installation.
The configuration option'xp_cmdshell'has been changed from 1 to 1. Please run the RECONFIGURE statement for installation.

Although the error is reported, but the value of xp_cmdshell has been set to 1, that is, the script execution takes effect!

Porting this code into the code, and then capturing and discarding exceptions through TRY CATCH, you can happily call xp_cmdshell.

==============================================================

Start with xp_cmdshell, and of course the relevant information can be obtained in a similar way!

For example, get sector information of disk:

--====================================
--Use xp_cmdshell Execution CMD command
--Getting disk sector information
EXEC sp_configure 'show advanced options',1 
GO
RECONFIGURE
GO
sp_configure 'xp_cmdshell',1 
GO
RECONFIGURE
GO
EXEC xp_cmdshell 'fsutil fsinfo ntfsinfo D: | find "each"';
GO
sp_configure 'xp_cmdshell',0 
GO
RECONFIGURE
GO
sp_configure 'show advanced options', 0 
GO
RECONFIGURE
GO

The operation effect is as follows:

Of course, you can use fsutil fsinfo ntfsinfo D: to get complete information, but more noteworthy are the lines above.

==============================================================

Feelings:

After so many years of SQL Server DBA, it is not easy to find a decent job of SQL SERVER DBA. On the one hand, it is caused by the current market trend, on the other hand, it is also caused by our own "death" of DBA. I see that many colleagues, including myself, are still in the "slash and burn" era. If there are problems, I have to point to the interface. Give the outside world the illusion that "SQL Server is easy to operate and maintain", and look at MySQL DBA, as long as you can pretend to "study the source code", immediately give people a "very compelling" catch-up, so the annual salary of 3.5 million is no longer a dream!

The slogan of Intelligent Operations and Maintenance has sounded. On the way to MySQL, I still remember that I am an old SQL Server DBA from time to time.

==============================================================

Chairman Chiang Kai-shek's words are encouraged by all the gentlemen.

Posted by cooler75 on Tue, 16 Jul 2019 11:46:51 -0700