I am using SQL Server 2008 and we are using the DMV's to find missing indexes. However, before I create the new index I am trying to figure out what proc/query is wanting that index. I want the most information I can get so I can make informed decision on my indexes. Sometimes the indexes SQL Server wants does not make sense to me. Does anyone know how I can figure out what wants it?
A:
You could run a profiler trace and check out the procedures that are running and their effectiveness in terms on index seeks / usage.
Rather than just do all indices for everyone, it is better to optimize the biggest problem - you usually will get the most benefit from this.
In the profiler trace, figure out which stored proc / tsql statement runs the most number of times and consumes the most resources. Those are the ones that you really want to go after.
Raj More
2010-06-04 12:12:12
This isn't really what I am asking. SQL's DMV (sys.dm_db_missing_index_details) gives a list of missing indexes. I want to know what would use the index if I created it.
BankZ
2010-06-04 12:19:17
@BankZ The reason I'm telling you to go the Profiler route is that the dm_db_missing_index_details tells you what index is missing, but not how much of a gain you are getting from it. IMO it would be a better option to optimize the queries that are actually causing the biggest issues.
Raj More
2010-06-04 12:29:56
I understand that and the DMV's are not the only thing we use. We do trace and do look at execution plans. However, you cannot trace 24/7 and there is a lot of information in the DMV's (and they do give you some estimates on the cost of the missing index and some benefits). However, that being said, it still doesn't answer my question :)
BankZ
2010-06-04 12:43:28
+2
A:
you could try something like this query, which lists the QueryText:
;WITH XMLNAMESPACES(DEFAULT N'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
, CachedPlans AS
(SELECT
RelOp.op.value(N'../../@NodeId', N'int') AS ParentOperationID
,RelOp.op.value(N'@NodeId', N'int') AS OperationID
,RelOp.op.value(N'@PhysicalOp', N'varchar(50)') AS PhysicalOperator
,RelOp.op.value(N'@LogicalOp', N'varchar(50)') AS LogicalOperator
,RelOp.op.value(N'@EstimatedTotalSubtreeCost ', N'float') AS EstimatedCost
,RelOp.op.value(N'@EstimateIO', N'float') AS EstimatedIO
,RelOp.op.value(N'@EstimateCPU', N'float') AS EstimatedCPU
,RelOp.op.value(N'@EstimateRows', N'float') AS EstimatedRows
,cp.plan_handle AS PlanHandle
,qp.query_plan AS QueryPlan
,st.TEXT AS QueryText
,cp.cacheobjtype AS CacheObjectType
,cp.objtype AS ObjectType
,cp.usecounts AS UseCounts
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
CROSS APPLY qp.query_plan.nodes(N'//RelOp') RelOp (op)
)
SELECT
PlanHandle
,ParentOperationID
,OperationID
,PhysicalOperator
,LogicalOperator
,UseCounts
,CacheObjectType
,ObjectType
,EstimatedCost
,EstimatedIO
,EstimatedCPU
,EstimatedRows
,QueryText
FROM CachedPlans
WHERE CacheObjectType = N'Compiled Plan'
AND PhysicalOperator IN ('nothing will ever match this one!'
--,'Assert'
--,'Bitmap'
--,'Clustered Index Delete'
--,'Clustered Index Insert'
,'Clustered Index Scan'
--,'Clustered Index Seek'
--,'Clustered Index Update'
--,'Compute Scalar'
--,'Concatenation'
--,'Constant Scan'
,'Deleted Scan'
--,'Filter'
--,'Hash Match'
,'Index Scan'
--,'Index Seek'
--,'Index Spool'
,'Inserted Scan'
--,'Merge Join'
--,'Nested Loops'
--,'Parallelism'
,'Parameter Table Scan'
--,'RID Lookup'
--,'Segment'
--,'Sequence Project'
--,'Sort'
--,'Stream Aggregate'
--,'Table Delete'
--,'Table Insert'
,'Table Scan'
--,'Table Spool'
--,'Table Update'
--,'Table-valued function'
--,'Top'
)
just add an ORDER BY on something like the combination of the UseCounts and EstimatedCost.
KM
2010-06-04 12:31:01
very interesting starting point. I will see what I can do with this and will let you know. Thank you.
BankZ
2010-06-04 12:53:05
A:
Here is what finally worked:
with xmlnamespaces(default 'http://schemas.microsoft.com/sqlserver/2004/07/showplan') , CachedPlans as (
select
query_plan,
n.value('../../../@StatementText' ,'varchar(1000)') as [Statement],
n.value('../../../@StatementSubTreeCost' ,'varchar(1000)') as [Cost],
n.value('../../../@StatementEstRows' ,'varchar(1000)') as [Rows],
n.value('@Impact' ,'float') as Impact,
n.value('MissingIndex[1]/@Database' ,'varchar(128)') as [Database],
n.value('MissingIndex[1]/@Table' ,'varchar(128)') as [TableName],
(
select dbo.concat(c.value('@Name' ,'varchar(128)'))
from n.nodes('MissingIndex/ColumnGroup[@Usage="EQUALITY"][1]') as t(cg)
cross apply cg.nodes('Column') as r(c)
) as equality_columns,
(
select dbo.concat(c.value('@Name' ,'varchar(128)'))
from n.nodes('MissingIndex/ColumnGroup[@Usage="INEQUALITY"][1]') as t(cg)
cross apply cg.nodes('Column') as r(c)
) as inequality_columns,
(
select dbo.concat(c.value('@Name' ,'varchar(128)'))
from n.nodes('MissingIndex/ColumnGroup[@Usage="INCLUDE"][1]') as t(cg)
cross apply cg.nodes('Column') as r(c)
) as include_columns
from (
select query_plan
from sys.dm_exec_cached_plans p
outer apply sys.dm_exec_query_plan(p.plan_handle) tp
) as tab(query_plan)
cross apply query_plan.nodes('//MissingIndexGroup') as q(n)
)
select *
from CachedPlans
BankZ
2010-06-05 10:39:07