Problem
One of the most important SQL Server DBA tasks is checking logical hard drive details such as the total capacity, free disk space, and used space by the SQL Server data and log files. In this tip we look at a method to view this information in a faster and simpler way.
Solution
We have some options when we need to check the logical disks installed in the server and/or the free disk space for each drive. For example, we can use Windows Explorer and check capacity, file system type, free space, and more. Another way is use the extended stored procedure xp_fixeddrives directly from SQL Server and check the drive and the free space on each drive. What about if we want to create some customized disk free space alerts in SQL Server? Or we need to create automatic daily reports with hard disk information from SQL Server which includes more details? Or what about if we need a new way to check more information than is provided by xp_fixeddrives?
In today’s tip I will explain a new stored procedure sp_fixeddrives and the table-value function ufn_LogicalDiskDrives I have created to get the following information I consider that most DBAs need to check on a daily basis:
- Drive Letter
- Volume Name
- File System type
- Capacity in GB
- Free Space in GB
- Free Space as a Percentage
- Total Data File Size
- Total Log File Size
It is important to note that the name of this new stored procedure starts with “sp_” that is “sp_fixeddrives” and it is different than “xp_fixeddrives” which starts with “xp_” (system extended stored procedure).
The new Table-Value Function ufn_LogicalDiskDrives gets hard drive properties calling OLE Automation procedures sp_OACreate and sp_OAGetProperty. The T-SQL creation code is the following:
USE [master]
GO
CREATE FUNCTION [dbo].[ufn_LogicalDiskDrives]()
RETURNS @DriveList Table
(
[DriveLetter] CHAR(1)
,[VolumeName] VARCHAR(255)
,[FileSystem] VARCHAR(50)
,[TotalSize] BIGINT
,[AvailableSpace] BIGINT
,[FreeSpace] BIGINT
)
AS
BEGIN
--Written by Percy Reyes
DECLARE @DriveLetter_ASCII_Code INT
DECLARE @FileSystemInstance INT
DECLARE @DriveCount INT
DECLARE @DriveCollection INT
DECLARE @Drive INT
DECLARE @Property NVARCHAR(100)
DECLARE @DriveLetter VARCHAR(1)
DECLARE @TotalSize BIGINT
DECLARE @AvailableSpace BIGINT
DECLARE @FreeSpace BIGINT
DECLARE @FileSystem VARCHAR(128)
DECLARE @VolumeName VARCHAR(128)
DECLARE @IsReady VARCHAR(5)
--Creating a File System Object for getting files or disk info.
exec sp_OACreate 'Scripting.FileSystemObject', @FileSystemInstance OUT
--Getting the collection of drives
exec sp_OAGetProperty @FileSystemInstance,'Drives', @DriveCollection OUT
--Getting the count of drives from collection
exec sp_OAGetProperty @DriveCollection,'Count', @DriveCount OUT
--starting from Drive "A" (ASCII 65)
SET @DriveLetter_ASCII_Code = 65
--to "Z" (ASCII 90)
WHILE @DriveLetter_ASCII_Code <= 90
BEGIN
---Creating the instance drive from Drive Collection
SET @Property = 'item("'+CHAR(@DriveLetter_ASCII_Code)+'")'
exec sp_OAGetProperty @DriveCollection,@Property, @Drive OUT
-- Getting the drive letter property
exec sp_OAGetProperty @Drive,'DriveLetter', @DriveLetter OUT
IF @DriveLetter = CHAR(@DriveLetter_ASCII_Code)
BEGIN
-- Getting more properties from each drive
exec sp_OAGetProperty @Drive,'VolumeName', @VolumeName OUT
exec sp_OAGetProperty @Drive,'FileSystem', @FileSystem OUT
exec sp_OAGetProperty @Drive,'TotalSize', @TotalSize OUT
exec sp_OAGetProperty @Drive,'AvailableSpace', @AvailableSpace OUT
exec sp_OAGetProperty @Drive,'FreeSpace', @FreeSpace OUT
exec sp_OAGetProperty @Drive,'IsReady' , @IsReady OUT;
IF @IsReady='True'
INSERT INTO @DriveList ( [DriveLetter],[TotalSize], [AvailableSpace],[FreeSpace],[FileSystem] ,[VolumeName] )
VALUES( @DriveLetter,@TotalSize,@AvailableSpace,@FreeSpace,@FileSystem,@VolumeName)
END
-- forward next drive
SET @DriveLetter_ASCII_Code = @DriveLetter_ASCII_Code +1
END
EXEC sp_OADestroy @Drive
EXEC sp_OADestroy @DriveCollection
RETURN
END
We will need to enable the “Ole Automation Procedures” configuration setting, so that we can use the new function otherwise it will fail.
EXECUTE sp_configure 'show advanced options', 1 RECONFIGURE WITH OVERRIDE GO EXECUTE sp_configure 'Ole Automation Procedures', 1 RECONFIGURE WITH OVERRIDE GO
Testing the new function ufn_LogicalDiskDrives:
SELECT * FROM [dbo].[ufn_LogicalDiskDrives]()

The new stored procedure sp_fixeddrives will be marked as a system object, so that we can call it from any database. This stored procedure will use the results returned by the function ufn_LogicalDiskDrives and add the Total Data File Size and Total Log File Size information in the final result. Here is the T-SQL creation code:
select ISNULL(DF.DriveLetter, LF.DriveLetter) DriveLetter, DF.DataSize_GB, LF.LogSize_GB from ( select left(physical_name, 1) DriveLetter, CAST(sum(size)/128.0/1024 AS DECIMAL(10,2)) DataSize_GB from sys.master_files where type_desc='ROWS' group by left(physical_name, 1), type_desc ) DF full outer join ( select left(physical_name, 1) DriveLetter, CAST(sum(size)/128.0/1024 AS DECIMAL(10,2)) LogSize_GB from sys.master_files where type_desc='LOG' group by left(physical_name, 1), type_desc ) LF on DF.DriveLetter=LF.DriveLetter
The result is showed here:

Finally, here is the T-SQL creation code of the stored procedure sp_fixeddrives.
USE [master] GO CREATE PROC dbo.sp_fixeddrives WITH ENCRYPTION AS BEGIN SET NOCOUNT ON -----Reporting Drive details .... select LDD.DriveLetter ,[VolumeName] ,[FileSystem] ,CAST(TotalSize/1024.0/1024/1024.0 AS DECIMAL(10,2)) [Capacity_GB] ,CAST(FreeSpace/1024.0/1024/1024.0 AS DECIMAL(10,2)) [FreeSpace_GB] ,CAST((CAST(FreeSpace/1024.0/1024/1024.0 AS DECIMAL(10,2))/CAST(TotalSize/1024.0/1024/1024.0 AS DECIMAL(10,2)) ) *100 AS DECIMAL(10,2)) [Free %] , ISNULL( DBFiles.DataSize_GB,0) DataSize_GB,ISNULL( DBFiles.LogSize_GB,0) LogSize_GB from master.[dbo].[ufn_LogicalDiskDrives]() LDD full outer join ( select ISNULL(DF.DriveLetter, LF.DriveLetter) DriveLetter, DF.DataSize_GB, LF.LogSize_GB from ( select left(physical_name, 1) DriveLetter, CAST(sum(size)/128.0/1024 AS DECIMAL(10,2)) DataSize_GB from sys.master_files where type_desc='ROWS' group by left(physical_name, 1), type_desc ) DF full outer join ( select left(physical_name, 1) DriveLetter, CAST(sum(size)/128.0/1024 AS DECIMAL(10,2)) LogSize_GB from sys.master_files where type_desc='LOG' group by left(physical_name, 1), type_desc ) LF on DF.DriveLetter=LF.DriveLetter ) DBFiles ON DBFiles.DriveLetter =LDD.DriveLetter SET NOCOUNT OFF END
We will mark the stored procedure as a system object:
USE [master] GO EXEC sys.sp_MS_marksystemobject 'sp_fixeddrives'
Here is the output after executing the code:
EXEC sp_fixeddrives

As you can see this new stored procedure sp_fixeddrives is very practical and useful to check logical hard disk information. Let me know if you have any questions or comments.
Next Steps
- Read these additional articles related to this tip:
- Ole Automation Procedures Server Configuration Option
- sp_OACreate (Transact-SQL)
- sp_OAGetProperty
- sp_configure (Transact-SQL)
- Check out these additional MSSQLTips.com resources:

Percy Reyes has a Bach. Systems Engineering and Certified Senior Database Administrator focused on Microsoft SQL Server Internals with over 15+ years of extensive experience managing database servers on SQL Server 6.5, 7.0, 2000, 2005, 2008, 2008 R2, 2012, 2014, 2016, and 2017. He has in-depth knowledge and strong experience in database administration of SQL Server including installation and upgrading the database server and client tools, allocation system storage and planning future storage requirements for the database system, Backup/Restore operations, Indexing, Statistics, Integrity Checks, Database Maintenance, Database Reduction, Storage Engine, Replication, Monitoring, Optimization and Performance Tuning, In-Memory OLTP, Security and Compliance, Scalability, High Availability, Mirroring, Log Shipping, AlwaysOn, Failover Clustering, FileStream, Partitioning Very Large Tables, Disaster Recovery, Troubleshooting, and internals issues. Percy is always contributing to the SQL Community via twitter (@percyreyes) and writing in his blog and also as a frequent speaker about SQL Server technologies for over 10+ years at local user group meetings, webcasts, and national conferences. He can be reached at https://www.percyreyes.com.
- MSSQLTips Awards: Rookie of the Year Contender – 2014


