Identifying Duplicate Indexes
本文是在阅读《Troubleshooting SQL Server》->Chapter 5: Missing Indexes->Identifying Duplicate Indexes时,文中提及两个处理重复索引的链接。此处整理链接文章,方便自己后期查看,详细内容请参考原文:How can you tell if an index is REALLY a duplicate? and Removing duplicate indexes
一、创建新的存储过程,返回表上的索引信息
Step 1: Setup sp_SQLskills_ExposeColsInIndexLevels
/*============================================================================
File: sp_SQLskills_ExposeColsInIndexLevels Summary: This procedure lists columns in the key vs. those in the leaf level
of a nonclustered index. This is dependent on whether or not the
nonclustered is UNIQUE as well as whether or not the table has a
clustered index. It also changes based on whether or not the
clustering key is UNIQUE. Date: May 2010 Version: SQL Server 2005/2008
------------------------------------------------------------------------------
Written by Paul S. Randal and Kimberly L. Tripp, SQLskills.com For more scripts and sample code, check out
http://www.SQLskills.com This script is intended only as a supplement to demos and lectures
given by SQLskills instructors. THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
============================================================================*/ USE master
go if OBJECTPROPERTY(OBJECT_ID('sp_SQLskills_ExposeColsInIndexLevels'), 'IsProcedure') = 1
drop procedure sp_SQLskills_ExposeColsInIndexLevels
go create procedure sp_SQLskills_ExposeColsInIndexLevels
(
@object_id int,
@index_id int,
@ColsInTree nvarchar(2126) OUTPUT,
@ColsInLeaf nvarchar(max) OUTPUT
)
AS
BEGIN
declare @nonclus_uniq int
, @column_id int
, @column_name nvarchar(260)
, @col_descending bit
, @colstr nvarchar (max); -- Get clustered index keys (id and name)
select sic.column_id, QUOTENAME(sc.name, N']') AS column_name, is_descending_key
into #clus_keys
from sys.index_columns AS sic
JOIN sys.columns AS sc
ON sic.column_id = sc.column_id AND sc.object_id = sic.object_id
where sic.[object_id] = @object_id
and [index_id] = 1; -- Get nonclustered index keys
select sic.column_id, sic.is_included_column, QUOTENAME(sc.name, N']') AS column_name, is_descending_key
into #nonclus_keys
from sys.index_columns AS sic
JOIN sys.columns AS sc
ON sic.column_id = sc.column_id
AND sc.object_id = sic.object_id
where sic.[object_id] = @object_id
and sic.[index_id] = @index_id; -- Is the nonclustered unique?
select @nonclus_uniq = is_unique
from sys.indexes
where [object_id] = @object_id
and [index_id] = @index_id; if (@nonclus_uniq = 0)
begin
-- Case 1: nonunique nonclustered index -- cursor for nonclus columns not included and
-- nonclus columns included but also clus keys
declare mycursor cursor for
select column_id, column_name, is_descending_key
from #nonclus_keys
where is_included_column = 0
open mycursor;
fetch next from mycursor into @column_id, @column_name, @col_descending;
WHILE @@FETCH_STATUS = 0
begin
select @colstr = ISNULL(@colstr, N'') + @column_name + CASE WHEN @col_descending = 1 THEN '(-)' ELSE N'' END + N', ';
fetch next from mycursor into @column_id, @column_name, @col_descending;
end
close mycursor;
deallocate mycursor; -- cursor over clus_keys if clustered
declare mycursor cursor for
select column_id, column_name, is_descending_key from #clus_keys
where column_id not in (select column_id from #nonclus_keys
where is_included_column = 0)
open mycursor;
fetch next from mycursor into @column_id, @column_name, @col_descending;
WHILE @@FETCH_STATUS = 0
begin
select @colstr = ISNULL(@colstr, N'') + @column_name + CASE WHEN @col_descending = 1 THEN '(-)' ELSE N'' END + N', ';
fetch next from mycursor into @column_id, @column_name, @col_descending;
end
close mycursor;
deallocate mycursor; select @ColsInTree = substring(@colstr, 1, LEN(@colstr) -1); -- find columns not in the nc and not in cl - that are still left to be included.
declare mycursor cursor for
select column_id, column_name, is_descending_key from #nonclus_keys
where column_id not in (select column_id from #clus_keys UNION select column_id from #nonclus_keys where is_included_column = 0)
open mycursor;
fetch next from mycursor into @column_id, @column_name, @col_descending;
WHILE @@FETCH_STATUS = 0
begin
select @colstr = ISNULL(@colstr, N'') + @column_name + CASE WHEN @col_descending = 1 THEN '(-)' ELSE N'' END + N', ';
fetch next from mycursor into @column_id, @column_name, @col_descending;
end
close mycursor;
deallocate mycursor; select @ColsInLeaf = substring(@colstr, 1, LEN(@colstr) -1); end -- Case 2: unique nonclustered
else
begin
-- cursor over nonclus_keys that are not includes
select @colstr = ''
declare mycursor cursor for
select column_id, column_name, is_descending_key from #nonclus_keys
where is_included_column = 0
open mycursor;
fetch next from mycursor into @column_id, @column_name, @col_descending;
WHILE @@FETCH_STATUS = 0
begin
select @colstr = ISNULL(@colstr, N'') + @column_name + CASE WHEN @col_descending = 1 THEN '(-)' ELSE N'' END + N', ';
fetch next from mycursor into @column_id, @column_name, @col_descending;
end
close mycursor;
deallocate mycursor; select @ColsInTree = substring(@colstr, 1, LEN(@colstr) -1); -- start with the @ColsInTree and add remaining columns not present...
declare mycursor cursor for
select column_id, column_name, is_descending_key from #nonclus_keys
WHERE is_included_column = 1;
open mycursor;
fetch next from mycursor into @column_id, @column_name, @col_descending;
WHILE @@FETCH_STATUS = 0
begin
select @colstr = ISNULL(@colstr, N'') + @column_name + CASE WHEN @col_descending = 1 THEN '(-)' ELSE N'' END + N', ';
fetch next from mycursor into @column_id, @column_name, @col_descending;
end
close mycursor;
deallocate mycursor; -- get remaining clustered column as long as they're not already in the nonclustered
declare mycursor cursor for
select column_id, column_name, is_descending_key from #clus_keys
where column_id not in (select column_id from #nonclus_keys)
open mycursor;
fetch next from mycursor into @column_id, @column_name, @col_descending;
WHILE @@FETCH_STATUS = 0
begin
select @colstr = ISNULL(@colstr, N'') + @column_name + CASE WHEN @col_descending = 1 THEN '(-)' ELSE N'' END + N', ';
fetch next from mycursor into @column_id, @column_name, @col_descending;
end
close mycursor;
deallocate mycursor; select @ColsInLeaf = substring(@colstr, 1, LEN(@colstr) -1);
select @colstr = '' end
-- Cleanup
drop table #clus_keys;
drop table #nonclus_keys; END;
GO exec sys.sp_MS_marksystemobject 'sp_SQLskills_ExposeColsInIndexLevels'
go
sp_SQLskills_ExposeColsInIndexLevels
这段代码用于返回tree/leaf结点的定义。
Step 2: Setup the replacement procedure for sp_helpindex -> sp_SQLskills_helpindex
/*============================================================================
File: sp_SQLskills_helpindex.sql Summary: So, what are the included columns?! Do you have a filter?
This is a MODIFIED sp_helpindex script that includes:
- Index IDs
- INCLUDEd columns
- Filtered index columns
- Leaf/tree details for rowstore indexes
- Columns defined for columnstore indexes
Additional details:
- whether or not the index is disabled
- Index usage stats Date: February 2016 Version: Works on 2008R2 - 2016 (requires: sp_SQLskills_ExposeColsInIndexLevels)
------------------------------------------------------------------------------
Written by Kimberly L. Tripp, SYSolutions, Inc. For more scripts and sample code, check out
http://www.SQLskills.com This script is intended only as a supplement to demos and lectures
given by SQLskills instructors. THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
============================================================================*/ USE [master];
GO IF OBJECTPROPERTY(OBJECT_ID(N'sp_SQLskills_helpindex')
, N'IsProcedure') = 1
DROP PROCEDURE [dbo].[sp_SQLskills_helpindex];
GO SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
GO CREATE PROCEDURE [dbo].[sp_SQLskills_helpindex]
@objname nvarchar(776) -- the table to check for indexes
as -- June 2016: Support for clustered columnstore as well as removing
-- errors around other index types (hekaton, XML, spatial, etc.)
--September 2013: Correct the output for columnstore indexes.
-- November 2010: Added a column to show if an index is disabled.
-- May 2010: Added tree/leaf columns to the output - this requires the
-- stored procedure: sp_SQLskills_ExposeColsInIndexLevels
-- March 2010: Added index_id to the output (ordered by index_id as well)
-- August 2008: Fixed a bug (missing begin/end block) AND I found
-- a few other issues that people hadn't noticed (yikes!)!
-- April 2008: Updated to add included columns to the output. -- See my blog for updates and/or additional information
-- http://www.SQLskills.com/blogs/Kimberly (Kimberly L. Tripp) set nocount on declare @objid int, -- the object id of the table
@indid smallint, -- the index id of an index
@type tinyint, -- the index type
@groupid int, -- the filegroup id of an index
@indname sysname,
@groupname sysname,
@status int,
@keys nvarchar(2126), --Length (16*max_identifierLength)+(15*2)+(16*3)
@inc_columns nvarchar(max),
@inc_Count smallint,
@loop_inc_Count smallint,
@dbname sysname,
@ignore_dup_key bit,
@is_unique bit,
@is_hypothetical bit,
@is_primary_key bit,
@is_unique_key bit,
@is_disabled bit,
@auto_created bit,
@no_recompute bit,
@filter_definition nvarchar(max),
@ColsInTree nvarchar(2126),
@ColsInLeaf nvarchar(max),
@ExecStr nvarchar(max) -- Check to see that the object names are local to the current database.
select @dbname = parsename(@objname,3)
if @dbname is null
select @dbname = db_name()
else if @dbname <> db_name()
begin
raiserror(15250,-1,-1)
return (1)
end -- Check to see the the table exists and initialize @objid.
select @objid = object_id(@objname)
if @objid is NULL
begin
raiserror(15009,-1,-1,@objname,@dbname)
return (1)
end -- OPEN CURSOR OVER INDEXES (skip stats: bug shiloh_51196)
declare ms_crs_ind cursor local static for
select i.index_id, i.[type], i.data_space_id, QUOTENAME(i.name, N']') AS name,
i.ignore_dup_key, i.is_unique, i.is_hypothetical, i.is_primary_key, i.is_unique_constraint,
s.auto_created, s.no_recompute, i.filter_definition, i.is_disabled
from sys.indexes as i
join sys.stats as s
on i.object_id = s.object_id
and i.index_id = s.stats_id
where i.object_id = @objid
open ms_crs_ind
fetch ms_crs_ind into @indid, @type, @groupid, @indname, @ignore_dup_key, @is_unique, @is_hypothetical,
@is_primary_key, @is_unique_key, @auto_created, @no_recompute, @filter_definition, @is_disabled -- IF NO INDEX, QUIT
if @@fetch_status < 0
begin
deallocate ms_crs_ind
raiserror(15472,-1,-1,@objname) -- Object does not have any indexes.
return (0)
end -- create temp tables
CREATE TABLE #spindtab
(
objname nvarchar(776), -- for exec sp_MSforeachtable identifying indexes on which table(Add By Uest)
index_name sysname collate database_default NOT NULL,
index_id int,
[type] tinyint,
ignore_dup_key bit,
is_unique bit,
is_hypothetical bit,
is_primary_key bit,
is_unique_key bit,
is_disabled bit,
auto_created bit,
no_recompute bit,
groupname sysname collate database_default NULL,
index_keys nvarchar(2126) collate database_default NULL, -- see @keys above for length descr
filter_definition nvarchar(max),
inc_Count smallint,
inc_columns nvarchar(max),
cols_in_tree nvarchar(2126),
cols_in_leaf nvarchar(max)
) CREATE TABLE #IncludedColumns
( RowNumber smallint,
[Name] nvarchar(128)
) -- Now check out each index, figure out its type and keys and
-- save the info in a temporary table that we'll print out at the end.
while @@fetch_status >= 0
begin
-- First we'll figure out what the keys are.
declare @i int, @thiskey nvarchar(131) -- 128+3 select @keys = QUOTENAME(index_col(@objname, @indid, 1), N']'), @i = 2
if (indexkey_property(@objid, @indid, 1, 'isdescending') = 1)
select @keys = @keys + '(-)' select @thiskey = QUOTENAME(index_col(@objname, @indid, @i), N']')
if ((@thiskey is not null) and (indexkey_property(@objid, @indid, @i, 'isdescending') = 1))
select @thiskey = @thiskey + '(-)' while (@thiskey is not null )
begin
select @keys = @keys + ', ' + @thiskey, @i = @i + 1
select @thiskey = QUOTENAME(index_col(@objname, @indid, @i), N']')
if ((@thiskey is not null) and (indexkey_property(@objid, @indid, @i, 'isdescending') = 1))
select @thiskey = @thiskey + '(-)'
end -- Second, we'll figure out what the included columns are.
select @inc_columns = NULL SELECT @inc_Count = count(*)
FROM sys.tables AS tbl
INNER JOIN sys.indexes AS si
ON (si.index_id > 0
and si.is_hypothetical = 0)
AND (si.object_id=tbl.object_id)
INNER JOIN sys.index_columns AS ic
ON (ic.column_id > 0
and (ic.key_ordinal > 0 or ic.partition_ordinal = 0 or ic.is_included_column != 0))
AND (ic.index_id=CAST(si.index_id AS int) AND ic.object_id=si.object_id)
INNER JOIN sys.columns AS clmns
ON clmns.object_id = ic.object_id
and clmns.column_id = ic.column_id
WHERE ic.is_included_column = 1 and
(si.index_id = @indid) and
(tbl.object_id= @objid) IF @inc_Count > 0
BEGIN
DELETE FROM #IncludedColumns
INSERT #IncludedColumns
SELECT ROW_NUMBER() OVER (ORDER BY clmns.column_id)
, clmns.name
FROM sys.tables AS tbl
INNER JOIN sys.indexes AS si
ON (si.index_id > 0
AND si.is_hypothetical = 0)
AND (si.object_id=tbl.object_id)
INNER JOIN sys.index_columns AS ic
ON (ic.column_id > 0
AND (ic.key_ordinal > 0 OR ic.partition_ordinal = 0 OR ic.is_included_column != 0))
AND (ic.index_id=CAST(si.index_id AS int) AND ic.object_id=si.object_id)
INNER JOIN sys.columns AS clmns
ON clmns.object_id = ic.object_id
AND clmns.column_id = ic.column_id
WHERE ic.is_included_column = 1 AND
(si.index_id = @indid) AND
(tbl.object_id= @objid) SELECT @inc_columns = QUOTENAME([Name], N']')
FROM #IncludedColumns
WHERE RowNumber = 1 SET @loop_inc_Count = 1 WHILE @loop_inc_Count < @inc_Count
BEGIN
SELECT @inc_columns = @inc_columns + ', ' + QUOTENAME([Name], N']')
FROM #IncludedColumns WHERE RowNumber = @loop_inc_Count + 1
SET @loop_inc_Count = @loop_inc_Count + 1
END
END SELECT @groupname = null SELECT @groupname = name
FROM sys.data_spaces
WHERE data_space_id = @groupid -- Get the column list for the tree and leaf level, for all nonclustered indexes IF
-- the table has a clustered index -- Clustered index is non-unique
IF @indid = 1 AND
(SELECT is_unique
FROM sys.indexes
WHERE index_id = 1
AND object_id = @objid) = 0
SELECT @ColsInTree = @keys + N', UNIQUIFIER', @ColsInLeaf = N'All columns "included" - the leaf level IS the data row, plus the UNIQUIFIER' -- Clustered index AND is unique
IF @indid = 1 AND (SELECT is_unique FROM sys.indexes WHERE index_id = 1 AND object_id = @objid) = 1
SELECT @ColsInTree = @keys, @ColsInLeaf = N'All columns "included" - the leaf level IS the data row.' -- Only run this for nonclustered indexes
IF @indid > 1
EXEC [sp_SQLskills_ExposeColsInIndexLevels] @objid, @indid, @ColsInTree OUTPUT, @ColsInLeaf OUTPUT -- Nonclustered, non-unique index with non-unique clustered ROWSTORE (type = 1) index
IF @indid > 1 AND @is_unique = 0
AND (SELECT is_unique
FROM sys.indexes
WHERE index_id = 1 AND type = 1
AND object_id = @objid) = 0
SELECT @ColsInTree = @ColsInTree + N', UNIQUIFIER', @ColsInLeaf = @ColsInLeaf + N', UNIQUIFIER' -- Nonclustered, unique index with non-unique clustered ROWSTORE (type = 1) index
IF @indid > 1 AND @is_unique = 1
AND (SELECT is_unique
FROM sys.indexes
WHERE index_id = 1 AND type = 1
AND object_id = @objid) = 0
SELECT @ColsInLeaf = @ColsInLeaf + N', UNIQUIFIER' -- Nonclustered rowstore on a heap
IF (@indid > 1 AND @type = 2)
AND (SELECT COUNT(*)
FROM sys.indexes
WHERE index_id = 1
AND object_id = @objid) = 0 -- table is a HEAP
BEGIN
IF (@is_unique_key = 0)
SELECT @ColsInTree = @keys + N', RID'
, @ColsInLeaf = @keys + N', RID' + CASE WHEN @inc_columns IS NOT NULL THEN N', ' + @inc_columns ELSE N'' END IF (@is_unique_key = 1)
SELECT @ColsInTree = @keys
, @ColsInLeaf = @keys + N', RID' + CASE WHEN @inc_columns IS NOT NULL THEN N', ' + @inc_columns ELSE N'' END
END -- INSERT ROW FOR INDEX insert into #spindtab values (@objname, @indname, @indid, @type, @ignore_dup_key, @is_unique, @is_hypothetical,
@is_primary_key, @is_unique_key, @is_disabled, @auto_created, @no_recompute, @groupname, @keys, @filter_definition, @inc_Count, @inc_columns, @ColsInTree, @ColsInLeaf) -- Next index
fetch ms_crs_ind into @indid, @type, @groupid, @indname, @ignore_dup_key, @is_unique, @is_hypothetical,
@is_primary_key, @is_unique_key, @auto_created, @no_recompute, @filter_definition, @is_disabled
end
deallocate ms_crs_ind -- DISPLAY THE RESULTS -- Query by version
IF CONVERT(smallint, SUBSTRING(CONVERT(varchar(50), SERVERPROPERTY('ProductVersion')), 1, 2)) < 16 select @ExecStr = N'select '
+ N'''table_name'' = objname, '
+ N'''index_id'' = index_id, '
+ N'''is_disabled'' = is_disabled, '
+ N'''index_name'' = index_name, '
+ N'''index_description'' = '
+ N' convert(varchar(210), ' --bits 16 off, 1, 2, 16777216 on, located on group '
+ N' case when index_id = 1 and type = 1 then ''clustered'' '
+ N' when index_id = 1 and type = 5 then ''clustered, columnstore'' '
+ N' when index_id > 1 and type = 2 then ''nonclustered'' '
+ N' when index_id > 1 and type = 6 then ''nonclustered, columnstore'' '
+ N' when index_id > 1 and type = 7 then ''nonclustered, HASH'' '
+ N' else ''new index type'' end '
+ N' + case when ignore_dup_key <>0 then '', ignore duplicate keys'' else '''' end '
+ N' + case when is_unique=1 then '', unique'' else '''' end '
+ N' + case when is_hypothetical <>0 then '', hypothetical'' else '''' end '
+ N' + case when is_primary_key <>0 then '', primary key'' else '''' end '
+ N' + case when is_unique_key <>0 then '', unique key'' else '''' end '
+ N' + case when auto_created <>0 then '', auto create'' else '''' end '
+ N' + case when no_recompute <>0 then '', stats no recompute'' else '''' end '
+ N' + case when groupname IS NOT NULL then '' located on '' + groupname else '''' end), '
+ N'''index_keys'' =
case when type IN (5, 6) then ''n/a, see columns_in_leaf for details''
else index_keys end,
''included_columns'' =
case when type IN (5, 6) then ''n/a, columnstore index''
when type = 7 then ''n/a, HASH''
else inc_columns end,
''filter_definition'' =
case when type IN (5, 6) then ''n/a, columnstore index''
when type = 7 then ''n/a, HASH''
else filter_definition end,
''columns_in_tree'' =
case when type IN (5, 6) then ''n/a, columnstore index''
when type = 7 then ''n/a, HASH''
else cols_in_tree end,
''columns_in_leaf'' =
case when type IN (5, 6) then ''Columns with columnstore index: '' + cols_in_leaf
when type = 7 then ''n/a, HASH''
else cols_in_leaf end from #spindtab
order by index_id ' IF CONVERT(smallint, SUBSTRING(CONVERT(varchar(50), SERVERPROPERTY('ProductVersion')), 1, 2)) >= 16 select @ExecStr = N'select '
+ N'''table_name'' = objname, '
+ N'''index_id'' = index_id, '
+ N'''is_disabled'' = is_disabled, '
+ N'''index_name'' = index_name, '
+ N'''index_description'' = '
+ N' convert(varchar(210), ' --bits 16 off, 1, 2, 16777216 on, located on group '
+ N' case when index_id = 1 and type = 1 then ''clustered'' '
+ N' when index_id = 1 and type = 5 then ''clustered, columnstore'' '
+ N' when index_id > 1 and type = 2 then ''nonclustered'' '
+ N' when index_id > 1 and type = 6 then ''nonclustered, columnstore'' '
+ N' when index_id > 1 and type = 7 then ''nonclustered, HASH'' '
+ N' else ''new index type'' end '
+ N' + case when ignore_dup_key <>0 then '', ignore duplicate keys'' else '''' end '
+ N' + case when is_unique=1 then '', unique'' else '''' end '
+ N' + case when is_hypothetical <>0 then '', hypothetical'' else '''' end '
+ N' + case when is_primary_key <>0 then '', primary key'' else '''' end '
+ N' + case when is_unique_key <>0 then '', unique key'' else '''' end '
+ N' + case when auto_created <>0 then '', auto create'' else '''' end '
+ N' + case when no_recompute <>0 then '', stats no recompute'' else '''' end '
+ N' + case when memory_optimized = 1 then '' located in MEMORY '' else '''' end '
+ N' + case when groupname IS NOT NULL AND
(memory_optimized = 0 OR memory_optimized IS NULL)
then '' located on '' + groupname else '''' end), '
+ N'''index_keys'' =
case when type IN (5, 6) then ''n/a, see columns_in_leaf for details''
else index_keys end,
''included_columns'' =
case when type IN (5, 6) then ''n/a, columnstore index''
when type = 7 then ''n/a, HASH''
else inc_columns end,
''filter_definition'' =
case when type IN (5, 6) then ''n/a, columnstore index''
when type = 7 then ''n/a, HASH''
else filter_definition end,
''columns_in_tree'' =
case when type IN (5, 6) then ''n/a, columnstore index''
when type = 7 then ''n/a, HASH''
else cols_in_tree end,
''columns_in_leaf'' =
case when type IN (5, 6) then ''Columns with columnstore index: '' + cols_in_leaf
when type = 7 then ''n/a, HASH''
else cols_in_leaf end from #spindtab
order by index_id ' --SELECT (@ExecStr)
EXEC (@ExecStr) return (0) -- sp_SQLskills_helpindex
go exec [sys].[sp_MS_marksystemobject] 'sp_SQLskills_helpindex'
go
sp_SQLskills_helpindex
返回结果添加table_name字段,方便 exec sp_MSforeachtable 得到库下所有表格上的索引信息
二、sp_helpindex VS sp_SQLskills_helpindex
首先得到测试数据
USE Test
GO
SELECT *
INTO Test.dbo.SalesOrderDetail
FROM AdventureWorks2008R2.Sales.SalesOrderDetail
GO
CREATE UNIQUE CLUSTERED INDEX CIX_SalesOrderDetailID ON dbo.SalesOrderDetail(SalesOrderDetailID)
CREATE INDEX IX_SalesOrderID1 ON dbo.SalesOrderDetail(SalesOrderID,rowguid,SalesOrderDetailID) INCLUDE(LineTotal)
CREATE INDEX IX_SalesOrderID2 ON dbo.SalesOrderDetail(SalesOrderID,rowguid) INCLUDE(LineTotal)
CREATE INDEX IX_SalesOrderID3 ON dbo.SalesOrderDetail(SalesOrderID,rowguid) INCLUDE(SalesOrderDetailID,LineTotal)
CREATE UNIQUE INDEX IX_SalesOrderID4 ON dbo.SalesOrderDetail(SalesOrderID,rowguid) INCLUDE(SalesOrderDetailID,LineTotal)
GO
-- Clean up
--DROP TABLE Test.dbo.SalesOrderDetail
Test Data
对比sp_helpindex和sp_SQLskills_helpindex的返回结果
USE Test
GO
sp_helpindex 'dbo.SalesOrderDetail'
GO
sp_SQLskills_helpindex 'dbo.SalesOrderDetail'
GO
helpindex
从返回的结果来看sp_helpindex的结果不够友好,只知道索引名称、描述、键值。而sp_SQLskills_helpindex返回有表名、索引ID、是否禁用、索引名称、描述、键值、包含列、过滤条件、tree/leaf定义。
从sp_SQLskills_helpindex结果可以得知IX_SalesOrderID1、IX_SalesOrderID2、IX_SalesOrderID3具有相同的tree/leaf定义,后面的两个索引重复。
三、如何得到重复索引
if OBJECT_ID('tempdb..#FindDupes') is not null
DROP TABLE #FindDupes;
GO CREATE TABLE #FindDupes
(
table_name varchar(776),
index_id int,
is_disabled bit,
index_name sysname,
index_description varchar(210),
index_keys nvarchar(2126),
included_columns nvarchar(max),
filter_definition nvarchar(max),
columns_in_tree nvarchar(2126),
columns_in_leaf nvarchar(max)
);
GO --INSERT INTO #FindDupes EXEC sp_SQLskills_helpindex 'dbo.SalesOrderDetail';
EXEC sp_MSforeachtable 'insert into #FindDupes exec sp_SQLskills_helpindex ''?'''
GO -- find duplicate indexes
SELECT t1.index_id, COUNT(*) AS 'Duplicate Indexes w/Lower Index_ID',
N'DROP INDEX '
+ t1.table_name
+ N'.'
+ t1.index_name AS 'Drop Index Statement'
FROM #FindDupes AS t1
INNER JOIN #FindDupes AS t2
ON t1.table_name=t2.table_name
AND t1.columns_in_tree = t2.columns_in_tree
AND t1.columns_in_leaf = t2.columns_in_leaf
AND ISNULL(t1.filter_definition, 1) = ISNULL(t2.filter_definition, 1)
AND PATINDEX('%unique%', t1.index_description) = PATINDEX('%unique%', t2.index_description)
AND t1.index_id > t2.index_id
GROUP BY t1.index_id, N'DROP INDEX ' + t1.table_name
+ N'.' + t1.index_name;
GO
Find Duplicate Indexes
具体数据表直接执行,使用sp_MSforeachtable返回整个数据库中各数据表的索引信息
重点关注大表,频繁写入的数据表,检查是否有重复索引。
Identifying Duplicate Indexes的更多相关文章
- 【译】The Accidental DBA:Troubleshooting Performance
最近重新翻看The Accidental DBA,将Troubleshooting Performance部分稍作整理,方便以后查阅.此篇是Part 2Part 1:The Accidental DB ...
- MS SQL巡检系列——检查重复索引
前言感想:一时兴起,突然想写一个关于MS SQL的巡检系列方面的文章,因为我觉得这方面的知识分享是有价值,也是非常有意义的.一方面,很多经验不足的人,对于巡检有点茫然,不知道要从哪些方面巡检,另外一方 ...
- MySQL检查重复索引工具-pt-duplicate-key-checker
在MySQL中是允许在同一个列上创建多个索引的,示例如下: mysql --socket=/tmp/mysql5173.sock -uroot -p mysql> SELECT VERSION( ...
- The Accidental DBA
The Accidental DBA (Day 1 of 30): Hardware Selection: CPU and Memory Considerations 本文大意: 全篇主要讲 ...
- py-faster-rcnn 训练参数修改(转)
faster rcnn默认有三种网络模型 ZF(小).VGG_CNN_M_1024(中).VGG16 (大) 训练图片大小为500*500,类别数1. 一. 修改VGG_CNN_M_1024模型配置文 ...
- MYSQL优化浅谈,工具及优化点介绍,mysqldumpslow,pt-query-digest,explain等
MYSQL优化浅谈 msyql是开发常用的关系型数据库,快速.稳定.开源等优点就不说了. 个人认为,项目上线,标志着一个项目真正的开始.从运维,到反馈,到再分析,再版本迭代,再优化… 这是一个漫长且考 ...
- 06. pt-duplicate-key-checker
| t01 | CREATE TABLE `t01` ( `pkid` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT ...
- py-faster-rcnn代码阅读2-config.py
简介 该文件指定了用于fast rcnn训练的默认config选项,不能随意更改,如需更改,应当用yaml再写一个config_file,然后使用cfg_from_file(filename)导入以 ...
- pt-duplicate-key-checker使用
pt-duplicate-key-checker工具可以检测表中重复的索引,对于一些业务量很大的表,而且开发不规范的情况下有用.基本用法: 看一下我们的测试表: mysql> desc new_ ...
随机推荐
- 【学习笔记】深入理解超时调用(setTimeout)和间歇调用(setInterval)
超时调用(setTimeout):在指定的毫秒数后调用函数或计算表达式. setTimeout(func, 1000); // func执行的函数,1000毫秒 间歇调用(setInterval):按 ...
- OpenCms JSP 模板开发——创建一个简单的JSP模板
OpenCms中的JSP模板就是一个普通的JSP页面,在特定的位置使用标签来包含内容,在这个的例子中,我们将要开发一个简单JSP模板,这个模板只是在内容(如<html>.<body& ...
- Javaweb分页功能简单实现
效果如下图 数据库中的数据 页面效果 首先,创建一个通用类Page,代码及 ...
- PyQt5实时汇率查询
用PyQt5实现了界面,使用urllib实时抓取ip138.com网站的汇率信息. import sys import urllib import urllib.request from PyQt5. ...
- MyBatis记录
记录一下MyBatis的几个模块大纲,除去缓存以及集合映射两个部分 Mybatis架构 1. mybatis配置 SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了myb ...
- jQuery圣诞雪花
<script type="text/javascript"> $(function(){ var d="<div class='y_snow'> ...
- C语言:min和max头文件
转自:http://www.cppblog.com/jince/archive/2010/09/14/126600.html min和max头文件 虽然说求最大值最小值函数在哪个头文件下并不是非常重要 ...
- Cannot convert 0 of type class java.lang.Integer to class java.lang.Boolean
org.apache.catalina.core.ApplicationDispatcher invoke SEVERE: Servlet.service() for servlet jsp thre ...
- 再起航,我的学习笔记之JavaScript设计模式16(享元模式)
### 享元模式 **享元模式(Flyweight):** 运用共享技术有效地支持大量的细粒度的对象,避免对象间拥有相同内容造成多余的开销. 上回我们在组合模式中创建了文章列表类,这次我们要向不同的文 ...
- python基础教程(二)
继续第一篇的内容,讲解,python的一些基本的东西. 注释 为了让别人能够更容易理解程序,使用注释是非常有效的,即使是自己回头再看旧代码也是一样. >>> #获得用户名: > ...