SQL Server CleanUp Database Queries

We have a costly SQL server database with bad performance. Specifically, some store procedures (join several tables on primary key, each table has ~10M rows) were executed for several miniutes. The execution plan showed that the index seek costs 90% of the total time. Finally we found the root cause is the indexes have very high degree of fragmentation. Since its DBA had changed many times, we need to analyze the database schemas, table disk usage and storage procedure dependency tables. Based on these results, we cleanup tables, store procedures and rebuild the indexes to improve the DB performance. Here are the queries to accomplish these tasks.

Get Disk Usage By Table

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
SELECT
    t.NAME AS TableName,
    s.Name AS SchemaName,
    p.rows AS RowCounts,
    SUM(a.total_pages) * 8 / 1024 AS TotalSpaceMB,
    SUM(a.used_pages) * 8 / 1024 AS UsedSpaceMB,
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 /1024 AS UnusedSpaceMB
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.NAME NOT LIKE 'dt%'
    AND t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255
GROUP BY
    t.Name, s.Name, p.Rows
ORDER BY
    UsedSpaceMB DESC

Sample result:

TableNameSchemaNameRowCountsTotalSpaceMBUsedSpaceMBUnusedSpaceMB
T1dbo256099719773597366369
T2dbo1579633747654376341201
T3dbo1071423256383531133269
T4dbo475337992600025310690
T5dbo1595652817750177428

Get Storage Procedure Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
DECLARE @dbname varchar(30) = '';

select
    db_name(st.dbid) as database_name,
    object_name(st.objectid) as name,
    p.size_in_bytes / 1024 as size_in_kb,
    p.usecounts,
    st.text
from sys.dm_exec_cached_plans p
cross apply sys.dm_exec_sql_text(p.plan_handle) st
where p.objtype = 'proc'
and st.dbid = db_id(@dbname)
order by p.usecounts desc

Sample result:

database_namenamesize_in_kbusecountstext
DProc_Upsert880039675743xxx
DProc_Update13682216916xxx
DProc_Select2801641531xxx

Get Storage Procedure Last Execution Time

1
2
3
4
5
6
7
SELECT DB_NAME(database_id)
    ,OBJECT_NAME(object_id,database_id) as procname
    ,cached_time
    ,last_execution_time
    ,execution_count
FROM sys.dm_exec_procedure_stats
order by procname

Sample result:

procnamecached_timelast_execution_timeexecution_count
CleanUpArticles2021-04-20 17:00:02.7102021-04-20 17:00:02.7103788
CleanUpTables2021-04-22 12:51:17.8072021-04-22 12:51:17.8076019
Proc_Select2021-04-19 02:09:41.8302021-04-19 02:09:41.8303827

Get Storage Procedure Dependency Table

1
2
3
4
5
SELECT DISTINCT p.name AS proc_name, t.name AS table_name
FROM sys.sql_dependencies d
INNER JOIN sys.procedures p ON p.object_id = d.object_id
INNER JOIN sys.tables     t ON t.object_id = d.referenced_major_id
ORDER BY proc_name, table_name

Sample result:

proc_nametable_name
P1T1
P2T1
P2T2

Get Amount of Index Fragmentation

Refer to: # Resolve index fragmentation by reorganizing or rebuilding indexes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
DECLARE @dbname varchar(30) = '';
DECLARE @tablename varchar(30) = '';

SELECT a.object_id, object_name(a.object_id) AS TableName,
    a.index_id, name AS IndedxName, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats
    (DB_ID(@dbname)
        , OBJECT_ID(@tablename)
        , NULL
        , NULL
        , NULL) AS a
INNER JOIN sys.indexes AS b
    ON a.object_id = b.object_id
    AND a.index_id = b.index_id;

Sample result:

object_idTableNameindex_idIndedxNameavg_fragmentation_in_percent
1733581214T11PK_dbo.T123.53719
1733581214T13key22.5999

Rebuild Index

1
2
3
ALTER INDEX [PK_dbo.T1]
    ON [dbo].[T1]
    REBUILD WITH (ONLINE = ON);

Reorganize Index

1
2
3
ALTER INDEX [PK_dbo.T1]
    ON [dbo].[T1]
    REORGANIZE;