Readable File Sizes: Converting Bytes to KB, MB and GB

Readable file sizes turn 1073741824 into 1.0 GiB, and that makes capacity talks much easier. The trick is to keep the raw bytes for sorting and math, and to format only at the very end. And to say which unit you mean.

A portion scoop beside a mound and equal portions of the same mixture

Pages to bytes, without the overflow

SQL Server stores file sizes as a count of 8 KB pages. To get bytes you multiply by 8192. That looks harmless until a database grows past 2 GB. The page count is an int, and int times int is still an int.

Here is a 300,000 page file, about 2.3 GB. Watch the plain multiplication fail and the bigint version succeed.

DECLARE @pages int = 300000;

BEGIN TRY
    SELECT @pages * 8192 AS Bytes;
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_MESSAGE() AS ErrorMessage;
END CATCH;

SELECT CONVERT(bigint, @pages) * 8192 AS Bytes;

The first attempt fails with error 8115, an arithmetic overflow. It works on your small test database and breaks on the big production one. The second query returns 2457600000. Always widen before you multiply.

With that rule, here is the size of every file in the current database, in pages, bytes and MiB. Your files and numbers will differ from mine.

SELECT name,
       size AS ConfiguredPages,
       CONVERT(bigint, size) * 8192 AS ConfiguredBytes,
       CONVERT(decimal(28, 2), size) * 8 / 1024 AS ConfiguredMiB
FROM sys.database_files
ORDER BY file_id;

This is configured size, the room the file has. It is not used space. Those are two different measurements, so give them two different column names.

Choose the unit on purpose

Now the formatting. KB, MB and GB are used for two things. Disk vendors count in thousands, so 1 GB is 1,000,000,000 bytes. Memory and most operating systems count in 1024s. The clear names for the 1024 kind are KiB, MiB and GiB.

I use the 1024 units and print the unit next to the number. The function returns the number and the unit as two columns, and gives NULL for NULL or negative input.

CREATE OR ALTER FUNCTION dbo.ReadableSizeDemo (@bytes decimal(28, 0))
RETURNS TABLE
AS RETURN
SELECT CAST(CASE WHEN @bytes IS NULL OR @bytes < 0 THEN NULL
                 WHEN @bytes >= 1073741824 THEN @bytes / 1073741824.0
                 WHEN @bytes >= 1048576 THEN @bytes / 1048576.0
                 WHEN @bytes >= 1024 THEN @bytes / 1024.0
                 ELSE @bytes END AS decimal(19, 1)) AS SizeValue,
       CASE WHEN @bytes IS NULL OR @bytes < 0 THEN NULL
            WHEN @bytes >= 1073741824 THEN 'GiB'
            WHEN @bytes >= 1048576 THEN 'MiB'
            WHEN @bytes >= 1024 THEN 'KiB'
            ELSE 'bytes' END AS SizeUnit;

Test it at the edges. Those are the places where formatting code usually goes wrong.

SELECT b.Bytes, f.SizeValue, f.SizeUnit
FROM (VALUES (CONVERT(decimal(28, 0), NULL)), (-1), (0), (1023),
             (1024), (1048576), (1073741824)) AS b(Bytes)
CROSS APPLY dbo.ReadableSizeDemo(b.Bytes) AS f
ORDER BY b.Bytes;
SQL Server results showing byte, KiB, MiB and GiB thresholds with invalid inputs as NULL
Seven test inputs: NULL and negative give NULL, then bytes, KiB, MiB and GiB at each boundary.

Read it row by row. NULL and -1 give NULL for both columns. Zero is 0.0 bytes and 1023 stays 1023.0 bytes. At 1024 it flips to 1.0 KiB, at 1048576 to 1.0 MiB, and at 1073741824 to 1.0 GiB.

KB or KiB, it is not the same

Two traps to know about

First, rounding. A value just below a boundary can round up to the next number inside its own unit. Second, the famous drive that “lost” space. A drive sold as 500 GB has 500,000,000,000 bytes. Read in 1024 units, it is smaller than 500.

SELECT b.Bytes, f.SizeValue, f.SizeUnit
FROM (VALUES (1048575), (500000000000)) AS b(Bytes)
CROSS APPLY dbo.ReadableSizeDemo(b.Bytes) AS f
ORDER BY b.Bytes;

1048575 bytes shows as 1024.0 KiB, not 1.0 MiB. That is a presentation choice you should decide, not discover. The 500 GB drive shows as 465.7 GiB. Nobody lost anything. The two sides just used different units.

One more rule keeps reports honest. Sort and compare on the byte column, never on the formatted text. As text, “9.0 KiB” sorts after “10.0 GiB”. Backup sizes in msdb are already in bytes, so do not multiply those by 8192 again.

When you are finished, drop the demo function.

DROP FUNCTION IF EXISTS dbo.ReadableSizeDemo;

Pick one unit policy, write it down, and let every capacity report use it.

A friendly size is not a new measurement, it is a clearer unit.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL Data Storage, SQL Datatype, SQL Server
Previous Post
Escaping Wildcards in LIKE: Searching for % and _ Literally
Next Post
Rows, Columns and Tables: How a Database Holds Information

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.