Lesson Learned #150: Calculating the space used by table and per column

Published 12-01-2020 10:16 AM 984 Views

Today, I got a service request that our customer needs to know the total data size per table and data size per column. 

I shared the following information with this customer.

 

  • My first idea was to identify the size per table (in terms of rows and KB). We executed this query:
SELECT 
    t.NAME AS TableName,
    s.Name AS SchemaName,
    p.rows AS RowCounts,
    SUM(a.total_pages) * 8 AS TotalSpaceKB, 
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
GROUP BY 
    t.Name, s.Name, p.Rows
ORDER BY 
    t.Name

 

  • We found that several tables are using too much data and have a lot of rows. 
  • We suggested to run the following script to the tables that they consider that are the main goal to review the size. 
  • Also, we mentioned that using DataLength per column our customer will have an impact in performance due to SQL SERVER needs to read all the rows of the tables and there is not needed to calculate for some data type like INT because it is fixed value that you could obtain the size basically multiple the max_lenght per number of rows. 
  • I think that this script is possible to improve a lot, for example, instead of running a TSQL command per execution, try to run every 50 or 60 columns, but, anyway, here is:

 

DECLARE @ObjectTable    sysname = 'Example'
DECLARE @Name           sysname
DECLARE @Type           INT
DECLARE @Total          BIGINT = 0
DECLARE @SQL            NVARCHAR(4000)
DECLARE @ParmDefinition nvarchar(100)= N'@Total BIGINT OUTPUT'  

DECLARE vColumns CURSOR FOR 
     SELECT C.name,T.USER_TYPE_ID from sys.columns c
     JOIN SYS.TYPES T
     ON C.USER_TYPE_ID=T.USER_TYPE_ID
       WHERE object_id=OBJECT_id(@ObjectTable) 
        ORDER BY 1; 
  
OPEN vColumns 
FETCH NEXT FROM vColumns 
INTO @Name, @Type
  
WHILE @@FETCH_STATUS = 0  
BEGIN  
       SET @Total=0
    SET @SQL = 'SELECT @Total = SUM(DATALENGTH(' + @Name + ')) FROM ' + @ObjectTable
    EXECUTE sp_executesql @SQL,@ParmDefinition, @Total = @Total OUTPUT;  
       PRINT 'Size:' + CONVERT(CHAR(20),@Total) + '|Type ID:' + CONVERT(CHAR(5),@Type) + '|Column Name:'+ CONVERT(CHAR(128), @Name)  + '|SQL:' + @SQL
       FETCH NEXT FROM vColumns INTO @Name, @Type
END   
CLOSE vColumns;  
DEALLOCATE vColumns;  

 

Enjoy!

%3CLINGO-SUB%20id%3D%22lingo-sub-1945517%22%20slang%3D%22en-US%22%3ELesson%20Learned%20%23150%3A%20Calculating%20the%20space%20used%20by%20table%20and%20per%20column%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1945517%22%20slang%3D%22en-US%22%3E%3CP%3EToday%2C%20I%20got%20a%20service%20request%20that%20our%20customer%20needs%20to%20know%20the%20total%20data%20size%20per%20table%20and%20data%20size%20per%20column.%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20shared%20the%20following%20information%20with%20this%20customer.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3EMy%20first%20idea%20was%20to%20identify%20the%20size%20per%20table%20(in%20terms%20of%20rows%20and%20KB).%20We%20executed%20this%20query%3A%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-sql%22%3E%3CCODE%3ESELECT%20%0A%20%20%20%20t.NAME%20AS%20TableName%2C%0A%20%20%20%20s.Name%20AS%20SchemaName%2C%0A%20%20%20%20p.rows%20AS%20RowCounts%2C%0A%20%20%20%20SUM(a.total_pages)%20*%208%20AS%20TotalSpaceKB%2C%20%0A%20%20%20%20SUM(a.used_pages)%20*%208%20AS%20UsedSpaceKB%2C%20%0A%20%20%20%20(SUM(a.total_pages)%20-%20SUM(a.used_pages))%20*%208%20AS%20UnusedSpaceKB%0AFROM%20%0A%20%20%20%20sys.tables%20t%0AINNER%20JOIN%20%20%20%20%20%20%0A%20%20%20%20sys.indexes%20i%20ON%20t.OBJECT_ID%20%3D%20i.object_id%0AINNER%20JOIN%20%0A%20%20%20%20sys.partitions%20p%20ON%20i.object_id%20%3D%20p.OBJECT_ID%20AND%20i.index_id%20%3D%20p.index_id%0AINNER%20JOIN%20%0A%20%20%20%20sys.allocation_units%20a%20ON%20p.partition_id%20%3D%20a.container_id%0ALEFT%20OUTER%20JOIN%20%0A%20%20%20%20sys.schemas%20s%20ON%20t.schema_id%20%3D%20s.schema_id%0AWHERE%20t.is_ms_shipped%20%3D%200%0A%20%20%20%20AND%20i.OBJECT_ID%20%26gt%3B%20255%20%0AGROUP%20BY%20%0A%20%20%20%20t.Name%2C%20s.Name%2C%20p.Rows%0AORDER%20BY%20%0A%20%20%20%20t.Name%0A%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3EWe%20found%20that%20several%20tables%20are%20using%20too%20much%20data%20and%20have%20a%20lot%20of%20rows.%26nbsp%3B%3C%2FLI%3E%0A%3CLI%3EWe%20suggested%20to%20run%20the%20following%20script%20to%20the%20tables%20that%20they%20consider%20that%20are%20the%20main%20goal%20to%20review%20the%20size.%26nbsp%3B%3C%2FLI%3E%0A%3CLI%3EAlso%2C%20we%20mentioned%20that%20using%20DataLength%20per%20column%20our%20customer%20will%20have%20an%20impact%20in%20performance%20due%20to%20SQL%20SERVER%20needs%20to%20read%20all%20the%20rows%20of%20the%20tables%20and%20there%20is%20not%20needed%20to%20calculate%20for%20some%20data%20type%20like%20INT%20because%20it%20is%20fixed%20value%20that%20you%20could%20obtain%20the%20size%20basically%20multiple%20the%20max_lenght%20per%20number%20of%20rows.%26nbsp%3B%3C%2FLI%3E%0A%3CLI%3EI%20think%20that%20this%20script%20is%20possible%20to%20improve%20a%20lot%2C%20for%20example%2C%20instead%20of%20running%20a%20TSQL%20command%20per%20execution%2C%20try%20to%20run%20every%2050%20or%2060%20columns%2C%20but%2C%20anyway%2C%20here%20is%3A%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-sql%22%3E%3CCODE%3EDECLARE%20%40ObjectTable%20%20%20%20sysname%20%3D%20'Example'%0ADECLARE%20%40Name%20%20%20%20%20%20%20%20%20%20%20sysname%0ADECLARE%20%40Type%20%20%20%20%20%20%20%20%20%20%20INT%0ADECLARE%20%40Total%20%20%20%20%20%20%20%20%20%20BIGINT%20%3D%200%0ADECLARE%20%40SQL%20%20%20%20%20%20%20%20%20%20%20%20NVARCHAR(4000)%0ADECLARE%20%40ParmDefinition%20nvarchar(100)%3D%20N'%40Total%20BIGINT%20OUTPUT'%20%20%0A%0ADECLARE%20vColumns%20CURSOR%20FOR%20%0A%20%20%20%20%20SELECT%20C.name%2CT.USER_TYPE_ID%20from%20sys.columns%20c%0A%20%20%20%20%20JOIN%20SYS.TYPES%20T%0A%20%20%20%20%20ON%20C.USER_TYPE_ID%3DT.USER_TYPE_ID%0A%20%20%20%20%20%20%20WHERE%20object_id%3DOBJECT_id(%40ObjectTable)%20%0A%20%20%20%20%20%20%20%20ORDER%20BY%201%3B%20%0A%20%20%0AOPEN%20vColumns%20%0AFETCH%20NEXT%20FROM%20vColumns%20%0AINTO%20%40Name%2C%20%40Type%0A%20%20%0AWHILE%20%40%40FETCH_STATUS%20%3D%200%20%20%0ABEGIN%20%20%0A%20%20%20%20%20%20%20SET%20%40Total%3D0%0A%20%20%20%20SET%20%40SQL%20%3D%20'SELECT%20%40Total%20%3D%20SUM(DATALENGTH('%20%2B%20%40Name%20%2B%20'))%20FROM%20'%20%2B%20%40ObjectTable%0A%20%20%20%20EXECUTE%20sp_executesql%20%40SQL%2C%40ParmDefinition%2C%20%40Total%20%3D%20%40Total%20OUTPUT%3B%20%20%0A%20%20%20%20%20%20%20PRINT%20'Size%3A'%20%2B%20CONVERT(CHAR(20)%2C%40Total)%20%2B%20'%7CType%20ID%3A'%20%2B%20CONVERT(CHAR(5)%2C%40Type)%20%2B%20'%7CColumn%20Name%3A'%2B%20CONVERT(CHAR(128)%2C%20%40Name)%20%20%2B%20'%7CSQL%3A'%20%2B%20%40SQL%0A%20%20%20%20%20%20%20FETCH%20NEXT%20FROM%20vColumns%20INTO%20%40Name%2C%20%40Type%0AEND%20%20%20%0ACLOSE%20vColumns%3B%20%20%0ADEALLOCATE%20vColumns%3B%20%20%0A%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EEnjoy!%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-1945517%22%20slang%3D%22en-US%22%3E%3CP%3EToday%2C%20I%20got%20a%20service%20request%20that%20our%20customer%20needs%20to%20know%20the%20total%20data%20size%20per%20table%20and%20data%20size%20per%20column.%26nbsp%3B%3C%2FP%3E%0A%3CP%3EI%20shared%20the%20following%20information%20with%20this%20customer.%3C%2FP%3E%3C%2FLINGO-TEASER%3E
Version history
Last update:
‎Dec 01 2020 10:16 AM
Updated by: