igelr
igelr

Reputation: 1731

Join several tables in SQL Server

I have a select statement which must join several tables including sys.indexes, sys.objects, sys.schemas and sys.dm_db_index_physical_stats in order to get a table like this

enter image description here

This is the statement:

select
    db_name() as [Database],
    sc.[name] as [Schema],
    obj.[name] as [Table],  
    idx.[type_desc] as [Index Type],
    idx.[name] as [Index Name],
    ips.[avg_fragmentation_in_percent] as [Fragmentation Percent]
from 
    sys.indexes as idx
inner join 
    sys.objects as obj on idx.object_id = obj.object_id
inner join 
    sys.schemas as sc  on obj.schema_id = sc.schema_id
inner join 
    sys.dm_db_index_physical_stats( NULL,NULL, NULL, NULL ,'LIMITED') AS ips on obj.object_id = ips.object_id 
where 
    idx.[name] is not null 
    and obj.[type] = 'u' or obj.[type] = 'v' 
order by 
    [Fragmentation Percent] desc

The problem is that this statement works wrong and includes some rows twice (does something like a cross join but not exactly that). Can anyone point out my mistake?

Thanks for your time!

Upvotes: 0

Views: 532

Answers (1)

Dan Guzman
Dan Guzman

Reputation: 46193

Use CROSS_APPLY instead of INNER JOIN. This will allow you to pass correlated values to the table-valued function. Added partition number to the result here:

SELECT
    db_name() as [Database],
    sc.[name] as [Schema],
    obj.[name] as [Table],  
    idx.[type_desc] as [Index Type],
    idx.[name] as [Index Name],
    ips.partition_number AS [Partition Number],
    ips.[avg_fragmentation_in_percent] as [Fragmentation Percent]
FROM sys.indexes as idx
INNER JOIN sys.objects as obj on idx.object_id = obj.object_id
INNER JOIN sys.schemas as sc  on obj.schema_id = sc.schema_id
CROSS APPLY sys.dm_db_index_physical_stats( DB_ID(), idx.object_id, idx.index_id, NULL ,'LIMITED') AS ips
ORDER BY [Fragmentation Percent] desc;

The mistakes with your original query are you didn't include index_id in the join to sys.dm_db_index_physical_stats and did not filter for objects in the current database (the same object_id may exist in different databases). The WHERE clause is superfluous so I removed it.

You should exclude small indexes for defragmentation purposes since those are allocated from mixed extents and will report high fragmentation even when freshly rebuilt.

Upvotes: 4

Related Questions