我一直在使用https://sqlperformance.com/2018/10/sql-performance/three-easy-sql-server-performance-wins上的一些查询 具体来说,就是缺少索引查询和缺少索引警告查询。
我没有尝试在两个结果集之间来回移动,而是尝试将它们组合成一个查询,因此我可以直接查看sys.dm_exec_query_plan()中缓存的查询计划与sys中缺少的索引相对应.dm_db_missing_index_details。
查询的当前迭代如下:
SELECT CONVERT(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) AS [index_advantage],
migs.last_user_seek, mid.[statement] AS [Database.Schema.Table], qps.ProcName, qps.objtype, qps.usecounts,
mid.equality_columns, mid.inequality_columns, mid.included_columns,
migs.unique_compiles, migs.user_seeks, migs.avg_total_user_cost, migs.avg_user_impact,
OBJECT_NAME(mid.[object_id]) AS [Table Name], p.rows AS [Table Rows]
,qps.query_plan
FROM sys.dm_db_missing_index_group_stats migs WITH (NOLOCK)
INNER JOIN sys.dm_db_missing_index_groups mig WITH (NOLOCK) ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details mid WITH (NOLOCK) ON mig.index_handle = mid.index_handle
INNER JOIN sys.partitions p WITH (NOLOCK) ON p.[object_id] = mid.[object_id]
Left Outer Join (
Select top 50 OBJECT_NAME(qp.objectid) ProcName, cp.objtype, qp.query_plan, cp.usecounts, d.referenced_id
From sys.dm_exec_cached_plans cp With (NOLOCK)
Cross Apply sys.dm_exec_query_plan(cp.plan_handle) qp
Left Outer Join sys.sql_expression_dependencies d With (NOLOCK) on d.referencing_id = qp.objectid
Where qp.dbid = DB_ID()
And cast(query_plan as nvarchar(max)) like N'%MissingIndex Database="#[' + db_name() + '#]" Schema="#[dbo#]" Table="#[' + d.referenced_entity_name +N'#]"%' escape '#'
Order By cp.usecounts desc
) qps on cast(qps.query_plan as nvarchar(max)) like N'%MissingIndex%'
+ Case When mid.equality_columns is null then ''
else 'Column Name="' + Replace(Replace(Replace(mid.equality_columns, ', ', 'Column Name="'), '[', '#['), ']', '#]%') end
+ Case When mid.inequality_columns is null then ''
else 'Column Name="' + Replace(Replace(Replace(mid.inequality_columns, ', ', 'Column Name="'), '[', '#['), ']', '#]%') end
+ Case When mid.included_columns is null then ''
else 'Column Name="' + Replace(Replace(Replace(mid.included_columns, ', ', 'Column Name="'), '[', '#['), ']', '#]%') end
escape '#'
And mid.object_id = qps.referenced_id
WHERE mid.database_id = DB_ID()
AND p.index_id < 2
ORDER BY index_advantage DESC OPTION (RECOMPILE);
我的第一次尝试使用外部应用而不是左联接,但是在生产数据库上执行时间很长(45分钟以上),因此我尝试了左联接。我不确定要花多长时间,但是我在15分钟后停止执行。
是否有可能从这两个中进行这样的查询?
答案 0 :(得分:0)
我知道这有点晚了,我还有很多紧迫的项目需要完成,而现在这些项目已经完成。
我最终编写了一个脚本,该脚本将遍历建议的最高索引并获取与它们相关联的所有缓存的查询计划。
/*********************************************************************************/
/** This script will take the top X missing indexes by advantage and get the **/
/** corresponding query plan(s) that generated it. The script excludes **/
/** the Include columns when finding the query plan. There is an optional **/
/** table parameter that will look at indexes for that table. This should be **/
/** executed against the specific database. **/
/*********************************************************************************/
Declare @table nvarchar(80),
@range int,
@verbose bit
/*******************************/
/* Enter desired values here. */
/*******************************/
Set @range = 15
Set @table = ''
Set @verbose = 1
-- The verbose parameter currently only outputs the XML search string when enabled
-- Begin script guts
Set NoCount On
-- This script goes through the following series of steps:
-- 1) Cache the current cached query plans with missing index warnings.
-- 2) Gets the top X missing indexes by index advantage.
-- 3) Loops through each of the missing indexes
-- a) Builds an XML-formatted string based on the data provided by the missing index
-- b) Gets the query plans that have that XML string in the query plan.
-- 4) Outputs the top missing indexes and the corresponding query plans
--
-- Note: Due to the nature of the query plan cache, some missing indexes may not
-- be associated with a query plan that is currently in the cache.
Declare @topMissingIndexes table (
id int identity,
index_advantage decimal(18,2),
[Database.Schema.Table] nvarchar(100),
equality_columns nvarchar(180),
inequality_columns nvarchar(180),
user_seeks bigint,
avg_total_user_cost decimal(18,2),
avg_user_impact float,
table_name nvarchar(128)
)
Declare @topQueries table (
id int identity,
missing_index_id int,
[object_name] nvarchar(128),
obj_type nvarchar(60),
usecounts int,
query_plan xml
)
Declare @queriesMissingIndex table (
[object_name] nvarchar(128),
obj_type nvarchar(60),
usecounts int,
query_plan xml,
query_plan_text nvarchar(max)
)
Declare @xmlText nvarchar(4000),
@loopCounter int,
@equality nvarchar(180),
@inequality nvarchar(180),
@table_name nvarchar(128),
@column_name nvarchar(80),
@column_id bigint
-- Cache list of query plans with missing indexes, since it takes a while for them to come up if they are requeried completely for each suggested index.
Insert @queriesMissingIndex
Select
Object_Name(objectid),
cp.objtype,
cp.usecounts,
query_plan,
Cast(query_plan AS nvarchar(max))
From sys.dm_exec_cached_plans cp With (NOLOCK)
Cross Apply sys.dm_exec_query_plan(cp.plan_handle) qp
Where Cast(query_plan AS nvarchar(max)) Like N'%MissingIndex%'
And dbid = DB_ID() Option (RECOMPILE);
-- Get the list of suggested indexes we want to work with
If IsNull(@table, N'') = N''
Insert @topMissingIndexes (
index_advantage,
[Database.Schema.Table],
equality_columns,
inequality_columns,
user_seeks,
avg_total_user_cost,
avg_user_impact,
table_name
)
Select Top (@range)
Convert(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) As index_advantage,
mid.[statement],
mid.equality_columns,
mid.inequality_columns,
migs.user_seeks,
Convert(decimal(18,2), migs.avg_total_user_cost),
migs.avg_user_impact,
Object_Name(mid.[object_id])
From sys.dm_db_missing_index_group_stats migs With (NOLOCK)
Inner Join sys.dm_db_missing_index_groups mig With (NOLOCK) On migs.group_handle = mig.index_group_handle
Inner Join sys.dm_db_missing_index_details mid WITH (NOLOCK) On mig.index_handle = mid.index_handle
Inner Join sys.partitions p With (NOLOCK) On p.[object_id] = mid.[object_id]
Where mid.database_id = DB_ID()
And p.index_id < 2
Order by index_advantage Desc Option (RECOMPILE);
Else
Insert @topMissingIndexes (
index_advantage,
[Database.Schema.Table],
equality_columns,
inequality_columns,
user_seeks,
avg_total_user_cost,
avg_user_impact,
table_name
)
Select Top (@range)
Convert(decimal(18,2), user_seeks * avg_total_user_cost * (avg_user_impact * 0.01)) As index_advantage,
mid.[statement],
mid.equality_columns,
mid.inequality_columns,
migs.user_seeks,
Convert(decimal(18,2), migs.avg_total_user_cost),
migs.avg_user_impact,
Object_Name(mid.[object_id])
From sys.dm_db_missing_index_group_stats migs With (NOLOCK)
Inner Join sys.dm_db_missing_index_groups mig With (NOLOCK) On migs.group_handle = mig.index_group_handle
Inner Join sys.dm_db_missing_index_details mid WITH (NOLOCK) On mig.index_handle = mid.index_handle
Inner Join sys.partitions p With (NOLOCK) On p.[object_id] = mid.[object_id]
Where mid.database_id = DB_ID()
And p.index_id < 2
And mid.[object_id] = Object_ID(@table)
Order by index_advantage Desc Option (RECOMPILE);
Set @loopCounter = 0
While Exists (Select * From @topMissingIndexes Where id > @loopCounter)
Begin
-- To reduce overhead when searching the cached query plan text, we will construct the missing index
-- xml based on the data we have so we won't need wild cards inside the xml search string.
Select
@loopCounter = id,
@table_name = table_name,
@equality = equality_columns,
@inequality = inequality_columns
From @topMissingIndexes
Where id = @loopCounter + 1
Set @xmlText = N'<MissingIndex Database="[' + DB_Name() + N']" Schema="[dbo]" Table="[' + @table_name + N']">'
-- Add the xml for equality columns
If Len(@equality) > 0
Begin
Set @xmlText += N'<ColumnGroup Usage="EQUALITY">'
-- Get rid of the brackets. We will need to add them back in when constructing the xml
Set @equality = Replace(Replace(Replace(@equality, '[', ''), ']', ''), ' ', '')
-- The logic will be different if we have to parse out multiple columns or not
If CharIndex(',', @equality) = 0
Set @column_name = @equality
Else
Set @column_name = Substring(@equality, 1, CharIndex(',', @equality) - 1)
Select @column_id = ColumnProperty(Object_ID(@table_name), @column_name, 'ColumnId')
Set @xmlText += N'<Column Name="[' + @column_name + N']" ColumnId="' + Convert(nvarchar, @column_id) + N'"/>'
While CharIndex(',', @equality) > 0
Begin
Set @equality = Substring(@equality, CharIndex(',', @equality) + 1, Len(@equality))
If CharIndex(',', @equality) = 0
Begin
Set @column_name = @equality
End
Else
Set @column_name = Substring(@equality, 1, CharIndex(',', @equality) - 1)
Select @column_id = ColumnProperty(Object_ID(@table_name), @column_name, 'ColumnId')
Set @xmlText += N'<Column Name="[' + @column_name + N']" ColumnId="' + Convert(nvarchar, @column_id) + N'"/>'
End
Set @xmlText += N'</ColumnGroup>'
End
-- Add the xml for inequality columns
If Len(@inequality) > 0
Begin
Set @xmlText += N'<ColumnGroup Usage="INEQUALITY">'
-- Get rid of the brackets. We will need to add them back in when constructing the xml
Set @inequality = Replace(Replace(Replace(@inequality, '[', ''), ']', ''), ' ', '')
-- The logic will be different if we have to parse out multiple columns or not
If CharIndex(',', @inequality) = 0
Set @column_name = @inequality
Else
Set @column_name = Substring(@inequality, 1, CharIndex(',', @inequality) - 1)
Select @column_id = ColumnProperty(Object_ID(@table_name), @column_name, 'ColumnId')
Set @xmlText += N'<Column Name="[' + @column_name + N']" ColumnId="' + Convert(nvarchar, @column_id) + N'"/>'
While CharIndex(',', @inequality) > 0
Begin
Set @inequality = Substring(@inequality, CharIndex(',', @inequality) + 1, Len(@inequality))
If CharIndex(',', @inequality) = 0
Set @column_name = @inequality
Else
Set @column_name = Substring(@inequality, 1, CharIndex(',', @inequality) - 1)
Select @column_id = ColumnProperty(Object_ID(@table_name), @column_name, 'ColumnId')
Set @xmlText += N'<Column Name="[' + @column_name + N']" ColumnId="' + Convert(nvarchar, @column_id) + N'"/>'
End
Set @xmlText += N'</ColumnGroup>'
End
If @verbose = 1
Begin
Print 'XML Text: '
Print ' ' + @xmlText
End
Set @xmlText = Replace(Replace(@xmlText, '[', '#['), ']', '#]')
Insert @topQueries (
missing_index_id,
[object_name],
obj_type,
usecounts,
query_plan
)
Select
@loopCounter,
[object_name],
obj_type,
usecounts,
query_plan
From @queriesMissingIndex
Where query_plan_text Like N'%' + @xmlText + N'%' Escape '#'
End
Select * From @topMissingIndexes
Order by index_advantage Desc
Select missing_index_id, [object_name], obj_type, usecounts, query_plan From @topQueries
Order by missing_index_id Asc, usecounts Desc
Set NoCount Off
以防其他任何人想要将这两个捆绑在一起。