A few handy SQL scripts for SQL Server users

From time to time, I have to take a look at the Microsoft SQL Server to check if everything is fine. In some cases, I have some housekeeping to do. Here is the list of useful scripts I use frequently to perform basic tasks.

Check database tables and their sizes

SELECT t.NAME AS TableName, s.Name AS SchemaName, p.rows, 
    SUM(a.total_pages) * 8 AS TotalSpaceKB, 
    CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS TotalSpaceMB, 
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS UsedSpaceMB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB, 
    CAST(ROUND(((SUM(a.total_pages) - SUM(a.used_pages)) * 8) / 1024.00, 2) AS NUMERIC(36, 2)) 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 
  AND t.is_ms_shipped = 0 
  AND i.OBJECT_ID > 255 
GROUP BY t.Name, s.Name, p.Rows 
ORDER BY TotalSpaceMB DESC, t.Name

Sometimes it is handy to check if some of the database tables are not too large. It is common, that tracking or logging tables got wild and take up a lot of space. In such a case, you can consider trimming them.

Are there any indexes?

Once you have your tables trimmed, you should check if there are indexes created on these tables. The reason to create indexes and how to choose which columns to include is a huge topic on its own. I will not try to explain it here. But here you have a query:

SELECT schema_name(t.schema_id) + '.' + t.[name] as table_or_view_name, 
    i.[name] as index_name,
    case when i.[type] = 1 then 'Clustered index'
        when i.[type] = 2 then 'Nonclustered unique index'
        when i.[type] = 3 then 'XML index'
        when i.[type] = 4 then 'Spatial index'
        when i.[type] = 5 then 'Clustered columnstore index'
        when i.[type] = 6 then 'Nonclustered columnstore index'
        when i.[type] = 7 then 'Nonclustered hash index'
        end as index_type,
    case when i.is_unique = 1 then 'Unique'
        else 'Not unique' end as [unique],
    case when t.[type] = 'U' then 'Table'
        when t.[type] = 'V' then 'View'
        end as [object_type]
FROM sys.objects t
  INNER JOIN sys.indexes i
    ON t.object_id = i.object_id
WHERE schema_name(t.schema_id) NOT IN ('sys')

Isn’t it too big?

If you are not sure where the database files are located and what are their sizes, you can use this one:

    name, size, size * 8/1024 'Size (MB)',
    max_size, database_id, type_desc, physical_name, 
    state_desc, is_media_read_only, is_read_only, 
    is_sparse, is_percent_growth, is_name_reserved, 
FROM sys.master_files

Oh, I need to truncate these files!

Attention! The query below should be used once your database is backed up. The logs will be truncated and you will lose recovery data for the transactions stored in these logs.

The query below is taking all log files and trying to truncate them. The system databases are skipped.


SET @ScriptToExecute = '';

@ScriptToExecute = @ScriptToExecute +
'USE ['+ d.name +']; alter database ['+ d.name +'] set recovery simple; alter database ['+ d.name +'] set recovery full; CHECKPOINT; DBCC SHRINKFILE (['+f.name+']);'
FROM sys.master_files f
  INNER JOIN sys.databases d 
    ON d.database_id = f.database_id
WHERE f.type = 1 
  AND d.database_id > 4 
  AND d.state_desc = 'ONLINE'

SELECT @ScriptToExecute ScriptToExecute

EXEC (@ScriptToExecute)