Edit 2007-8-9:Added code to show database file sizes.  Not really closely related to tables sizes, but a lot of the people who need this want to know why their database it so large, so it may help to know which files, especially the logs, are so large, and if the files have empty space in them.-- Script to analyze table space usage using the-- output from the sp_spaceused stored procedure-- Works with SQL 7.0, 2000, and 2005set nocount onprint 'Show Size, Space Used, Unused Space, Type, and Name of all database files'select	[FileSizeMB]	=		convert(numeric(10,2),sum(round(a.size/128.,2))),        [UsedSpaceMB]	=		convert(numeric(10,2),sum(round(fileproperty( a.name,'SpaceUsed')/128.,2))) ,        [UnusedSpaceMB]	=		convert(numeric(10,2),sum(round((a.size-fileproperty( a.name,'SpaceUsed'))/128.,2))) ,	[Type] =		case when a.groupid is null then '' when a.groupid = 0 then 'Log' else 'Data' end,	[DBFileName]	= isnull(a.name,'*** Total for all files ***')from	sysfiles agroup by	groupid,	a.name	with rolluphaving	a.groupid is null or	a.name is not nullorder by	case when a.groupid is null then 99 when a.groupid = 0 then 0 else 1 end,	a.groupid,	case when a.name is null then 99 else 0 end,	a.namecreate table #TABLE_SPACE_WORK(	TABLE_NAME 	sysname		not null ,	TABLE_ROWS 	numeric(18,0)	not null ,	RESERVED 	varchar(50) 	not null ,	DATA 		varchar(50) 	not null ,	INDEX_SIZE 	varchar(50) 	not null ,	UNUSED 		varchar(50) 	not null ,)create table #TABLE_SPACE_USED(	Seq		int		not null		identity(1,1)	primary key clustered,	TABLE_NAME 	sysname		not null ,	TABLE_ROWS 	numeric(18,0)	not null ,	RESERVED 	varchar(50) 	not null ,	DATA 		varchar(50) 	not null ,	INDEX_SIZE 	varchar(50) 	not null ,	UNUSED 		varchar(50) 	not null ,)create table #TABLE_SPACE(	Seq		int		not null	identity(1,1)	primary key clustered,	TABLE_NAME 	SYSNAME 	not null ,	TABLE_ROWS 	int	 	not null ,	RESERVED 	int	 	not null ,	DATA 		int	 	not null ,	INDEX_SIZE 	int	 	not null ,	UNUSED 		int	 	not null ,	USED_MB				numeric(18,4)	not null,	USED_GB				numeric(18,4)	not null,	AVERAGE_BYTES_PER_ROW		numeric(18,5)	null,	AVERAGE_DATA_BYTES_PER_ROW	numeric(18,5)	null,	AVERAGE_INDEX_BYTES_PER_ROW	numeric(18,5)	null,	AVERAGE_UNUSED_BYTES_PER_ROW	numeric(18,5)	null,)declare @fetch_status intdeclare @proc 	varchar(200)select	@proc	= rtrim(db_name())+'.dbo.sp_spaceused'declare Cur_Cursor cursor localforselect	TABLE_NAME	= 	rtrim(TABLE_SCHEMA)+'.'+rtrim(TABLE_NAME)from	INFORMATION_SCHEMA.TABLES where	TABLE_TYPE	= 'BASE TABLE'order by	1open Cur_Cursordeclare @TABLE_NAME 	varchar(200)select @fetch_status = 0while @fetch_status = 0	begin	fetch next from Cur_Cursor	into		@TABLE_NAME	select @fetch_status = @@fetch_status	if @fetch_status <> 0		begin		continue		end	truncate table #TABLE_SPACE_WORK	insert into #TABLE_SPACE_WORK		(		TABLE_NAME,		TABLE_ROWS,		RESERVED,		DATA,		INDEX_SIZE,		UNUSED		)	exec @proc @objname = 		@TABLE_NAME ,@updateusage = 'true'	-- Needed to work with SQL 7	update #TABLE_SPACE_WORK	set		TABLE_NAME = @TABLE_NAME	insert into #TABLE_SPACE_USED		(		TABLE_NAME,		TABLE_ROWS,		RESERVED,		DATA,		INDEX_SIZE,		UNUSED		)	select		TABLE_NAME,		TABLE_ROWS,		RESERVED,		DATA,		INDEX_SIZE,		UNUSED	from		#TABLE_SPACE_WORK	end 	--While endclose Cur_Cursordeallocate Cur_Cursorinsert into #TABLE_SPACE	(	TABLE_NAME,	TABLE_ROWS,	RESERVED,	DATA,	INDEX_SIZE,	UNUSED,	USED_MB,	USED_GB,	AVERAGE_BYTES_PER_ROW,	AVERAGE_DATA_BYTES_PER_ROW,	AVERAGE_INDEX_BYTES_PER_ROW,	AVERAGE_UNUSED_BYTES_PER_ROW	)select	TABLE_NAME,	TABLE_ROWS,	RESERVED,	DATA,	INDEX_SIZE,	UNUSED,	USED_MB			=		round(convert(numeric(25,10),RESERVED)/		convert(numeric(25,10),1024),4),	USED_GB			=		round(convert(numeric(25,10),RESERVED)/		convert(numeric(25,10),1024*1024),4),	AVERAGE_BYTES_PER_ROW	=		case		when TABLE_ROWS <> 0		then round(		(1024.000000*convert(numeric(25,10),RESERVED))/		convert(numeric(25,10),TABLE_ROWS),5)		else null		end,	AVERAGE_DATA_BYTES_PER_ROW	=		case		when TABLE_ROWS <> 0		then round(		(1024.000000*convert(numeric(25,10),DATA))/		convert(numeric(25,10),TABLE_ROWS),5)		else null		end,	AVERAGE_INDEX_BYTES_PER_ROW	=		case		when TABLE_ROWS <> 0		then round(		(1024.000000*convert(numeric(25,10),INDEX_SIZE))/		convert(numeric(25,10),TABLE_ROWS),5)		else null		end,	AVERAGE_UNUSED_BYTES_PER_ROW	=		case		when TABLE_ROWS <> 0		then round(		(1024.000000*convert(numeric(25,10),UNUSED))/		convert(numeric(25,10),TABLE_ROWS),5)		else null		endfrom	(	select		TABLE_NAME,		TABLE_ROWS,		RESERVED	= 		convert(int,rtrim(replace(RESERVED,'KB',''))),		DATA		= 		convert(int,rtrim(replace(DATA,'KB',''))),		INDEX_SIZE	= 		convert(int,rtrim(replace(INDEX_SIZE,'KB',''))),		UNUSED		= 		convert(int,rtrim(replace(UNUSED,'KB','')))	from		#TABLE_SPACE_USED aa	) aorder by	TABLE_NAMEprint 'Show results in descending order by size in MB'select * from #TABLE_SPACE order by USED_MB descgodrop table #TABLE_SPACE_WORKdrop table #TABLE_SPACE_USED drop table #TABLE_SPACE
CODO ERGO SUM