SHARE
Facebook X Pinterest WhatsApp

Find space Usage by Table , Schema in SQL Server 2005 and 2008

Mar 18, 2008

Often SQL Server Database administrators struggle with hard disk space and constantly strive to clean up tables and schemas, writing many queries to find which table and schema uses the most hard disk space.


This article illustrates how to query system catalogs to find the space usage, which helps database administrators identify the schemas and tables that are using the most space in order to archive the old data and purge un-necessary data.


Note: The scripts listed in the article were tested in SQL Server 2005 and SQL Server 2008 CTP5.


Step 1


Logon to the SQL Server Instance [SQL Server 2005 or SQL Server 2008] as shown below. [Refer Fig 1.0]




Fig 1.0


Step 2


Navigate to the database that you want to get the space information on, as shown below. [Refer Fig 1.1]

Use AdventureWorks
go



Fig 1.1


Step 3


Copy and paste the code below to your Query Window and execute it as shown below. [Fig 1.2]

begin try
SELECT
 (row_number() over(order by a3.name, a2.name))%2 as l1,
 a3.name AS [schemaname],
 a2.name AS [tablename],
 a1.rows as row_count,
 (a1.reserved + ISNULL(a4.reserved,0))* 8 AS reserved,
 a1.data * 8 AS data,
 (CASE WHEN (a1.used + ISNULL(a4.used,0)) > a1.data THEN
   (a1.used + ISNULL(a4.used,0)) – a1.data ELSE 0 END) * 8 AS index_size,
 (CASE WHEN (a1.reserved + ISNULL(a4.reserved,0)) > a1.used THEN
   (a1.reserved + ISNULL(a4.reserved,0)) – a1.used ELSE 0 END) * 8 AS unused
FROM
 (SELECT
  ps.object_id,
  SUM (
   CASE
    WHEN (ps.index_id < 2) THEN row_count
    ELSE 0
   END
   ) AS [rows],
  SUM (ps.reserved_page_count) AS reserved,
  SUM (
   CASE
     WHEN (ps.index_id < 2) THEN
   (ps.in_row_data_page_count + ps.lob_used_page_count + ps.row_overflow_used_page_count)
     ELSE (ps.lob_used_page_count + ps.row_overflow_used_page_count)
   END
   ) AS data,
  SUM (ps.used_page_count) AS used
 FROM sys.dm_db_partition_stats ps
 GROUP BY ps.object_id) AS a1
LEFT OUTER JOIN
 (SELECT
  it.parent_id,
  SUM(ps.reserved_page_count) AS reserved,
  SUM(ps.used_page_count) AS used
  FROM sys.dm_db_partition_stats ps
  INNER JOIN sys.internal_tables it ON (it.object_id = ps.object_id)
  WHERE it.internal_type IN (202,204)
  GROUP BY it.parent_id) AS a4 ON (a4.parent_id = a1.object_id)
INNER JOIN sys.all_objects a2  ON ( a1.object_id = a2.object_id )
INNER JOIN sys.schemas a3 ON (a2.schema_id = a3.schema_id)
WHERE a2.type <> ‘S’ and a2.type <> ‘IT’
ORDER BY a3.name, a2.name
end try
begin catch
select
 -100 as l1
, 1 as schemaname
,       ERROR_NUMBER() as tablename
,       ERROR_SEVERITY() as row_count
,       ERROR_STATE() as reserved
,       ERROR_MESSAGE() as data
,       1 as index_size
,   1 as unused
end catch



Fig 1.2


On executing the above code, you will see the table space usage report shown below. [Refer Fig 1.3]


Result

l1, schemaname, tablename, row_count, reserved, data, index_size, unused
1, dbo, AWBuildVersion, 1, 16, 8, 8, 0
0, dbo, DatabaseLog, 451, 1696, 1616, 24, 56
1, dbo, ErrorLog, 0, 0, 0, 0, 0
0, HumanResources, Department, 16, 32, 8, 24, 0
1, HumanResources, Employee, 290, 176, 56, 120, 0
0, HumanResources, EmployeeAddress, 290, 48, 16, 32, 0
1, HumanResources, EmployeeDepartmentHistory, 296, 64, 16, 48, 0
0, HumanResources, EmployeePayHistory, 316, 32, 16, 16, 0
1, HumanResources, JobCandidate, 13, 232, 128, 32, 72
0, HumanResources, Shift, 3, 48, 8, 40, 0
1, Person, Address, 19614, 4960, 2224, 2440, 296
0, Person, AddressType, 6, 48, 8, 40, 0
.
.
.
.
0, Sales, SalesReason, 10, 16, 8, 8, 0
1, Sales, SalesTaxRate, 29, 48, 8, 40, 0
0, Sales, SalesTerritory, 10, 48, 8, 40, 0
1, Sales, SalesTerritoryHistory, 17, 32, 8, 24, 0
0, Sales, ShoppingCartItem, 3, 32, 8, 24, 0
1, Sales, SpecialOffer, 16, 32, 8, 24, 0
0, Sales, SpecialOfferProduct, 538, 88, 24, 64, 0
1, Sales, Store, 701, 1496, 808, 608, 608
0, Sales, StoreContact, 753, 160, 40, 120, 0
(72 row(s) affected)



Fig 1.3


The Above script can be modified using the SUM, COUNT function and the group by clause to produce schema space usage.


Copy and paste the below code to your query window and execute it. [Refer Fig 1.4]

begin try
SELECT
 –(row_number() over(order by a3.name, a2.name))%2 as l1,
 a3.name AS [schemaname],
 count(a2.name ) as NumberOftables,
 sum(a1.rows) as row_count,
 sum((a1.reserved + ISNULL(a4.reserved,0))* 8) AS reserved,
 sum(a1.data * 8) AS data,
 sum((CASE WHEN (a1.used + ISNULL(a4.used,0)) > a1.data THEN
   (a1.used + ISNULL(a4.used,0)) – a1.data ELSE 0 END) * 8 )AS index_size,
 sum((CASE WHEN (a1.reserved + ISNULL(a4.reserved,0)) > a1.used THEN
   (a1.reserved + ISNULL(a4.reserved,0)) – a1.used ELSE 0 END) * 8) AS unused
FROM
 (SELECT
  ps.object_id,
  SUM (
   CASE
    WHEN (ps.index_id < 2) THEN row_count
    ELSE 0
   END
   ) AS [rows],
  SUM (ps.reserved_page_count) AS reserved,
  SUM (
   CASE
    WHEN (ps.index_id < 2) THEN
     (ps.in_row_data_page_count + ps.lob_used_page_count + ps.row_overflow_used_page_count)
    ELSE (ps.lob_used_page_count + ps.row_overflow_used_page_count)
   END
   ) AS data,
  SUM (ps.used_page_count) AS used
 FROM sys.dm_db_partition_stats ps
 GROUP BY ps.object_id) AS a1
LEFT OUTER JOIN
 (SELECT
  it.parent_id,
  SUM(ps.reserved_page_count) AS reserved,
  SUM(ps.used_page_count) AS used
  FROM sys.dm_db_partition_stats ps
  INNER JOIN sys.internal_tables it ON (it.object_id = ps.object_id)
  WHERE it.internal_type IN (202,204)
  GROUP BY it.parent_id) AS a4 ON (a4.parent_id = a1.object_id)
INNER JOIN sys.all_objects a2  ON ( a1.object_id = a2.object_id )
INNER JOIN sys.schemas a3 ON (a2.schema_id = a3.schema_id)
WHERE a2.type <> ‘S’ and a2.type <> ‘IT’
group by a3.name
ORDER BY a3.name
end try
begin catch
select
 -100 as l1
, 1 as schemaname
,       ERROR_NUMBER() as tablename
,       ERROR_SEVERITY() as row_count
,       ERROR_STATE() as reserved
,       ERROR_MESSAGE() as data
,       1 as index_size
,   1 as unused
end catch



Fig 1.4


This will produce the schema space usage result, as shown below. [Refer Fig 1.5]




Fig 1.5


Result

schemaname, NumberOftables, row_count, reserved, data, index_size, unused
dbo, 3, 452, 1712, 1624, 32, 56
HumanResources, 7, 1224, 632, 248, 312, 72
Person, 7, 40212, 12088, 6792, 4616, 720
Production, 26, 351655, 37008, 25112, 9640, 2424
Purchasing, 7, 13563, 1512, 920, 496, 96
Sales, 22, 291557, 111016, 46224, 61848, 53488
(6 row(s) affected)

Conclusion


By executing the above queries, you can get the space usage result on a table by table basis or by schema.


» See All Articles by Columnist MAK

Recommended for you...

Best Online Courses to Learn SQL
Ronnie Payne
Sep 23, 2022
Best Courses for Database Administrators
Ronnie Payne
Jul 22, 2022
Tip 74 – Changing Cost Threshold for Parallelism
Gregory Larsen
Feb 24, 2021
How Many Databases Can You Name?
Brad Jones
May 11, 2020
Database Journal Logo

DatabaseJournal.com publishes relevant, up-to-date and pragmatic articles on the use of database hardware and management tools and serves as a forum for professional knowledge about proprietary, open source and cloud-based databases--foundational technology for all IT systems. We publish insightful articles about new products, best practices and trends; readers help each other out on various database questions and problems. Database management systems (DBMS) and database security processes are also key areas of focus at DatabaseJournal.com.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.