我应该删除这个聚集索引吗?

Mag*_*ier 1 index sql-server clustered-index sql-server-2008-r2 nonclustered-index

我有很多设计类似于以下(稍微简化的)示例的表格。不同服务器上的原始表有 50,000 到 200,000 行(大约 12,000 - 150,000 页)。

CREATE TABLE [dbo].[PWS] (
    [ID]             UNIQUEIDENTIFIER NOT NULL,
    [PWFID]     UNIQUEIDENTIFIER NOT NULL,
    [SuID]         UNIQUEIDENTIFIER NULL,
    [Title]         varchar(500),
    [SITime]     DATETIME         NULL,
    [Status]      INT,
    [ActionUserID]                UNIQUEIDENTIFIER NULL,
 --   […]
    CONSTRAINT [PK_PWS] PRIMARY KEY NONCLUSTERED ([ID] ASC) WITH (FILLFACTOR = 90),
    CONSTRAINT [FK_PWS_PW] FOREIGN KEY ([PWFID]) REFERENCES [dbo].[PW] ([ID]) ON DELETE CASCADE NOT FOR REPLICATION,
    CONSTRAINT [FK_PWS_Status] FOREIGN KEY ([Status]) REFERENCES [dbo].[Status] ([Status]) NOT FOR REPLICATION
);

CREATE CLUSTERED INDEX [IX_Clustered]
    ON [dbo].[PWS]([PWFID] ASC) WITH (FILLFACTOR = 90);
Run Code Online (Sandbox Code Playgroud)

因此,这些表使用唯一标识符作为非聚集索引的主键,并使用另一个不唯一的唯一标识符作为聚集索引。JOINS 通常发生在这个聚集键上。

不幸的是,大多数查询都不满足于加入,但也通过选择它们将更多的列包含到输出中。

我实际看到的样本是:

  • 加入:PWFID
  • 选择:标题、SI时间

这导致使用聚集索引,然后是Key Lookups。现在,在审查所有这些查询并尝试优化它们的过程中,我正在寻找减少这些关键查找的选项。所以我正处于讨论的中间:是否聚集索引。

由于在聚集索引旁边有主键,我在这个表上已经有两个或多或少“无用”的索引,因为现在加入和获取数据的查询无法覆盖 - 而且我什至无法调整索引已经到位以覆盖它们。相反,我需要该表上的另一个额外的非聚集索引来覆盖查询。

这让我想知道摆脱聚集索引并使这个表成为堆是否有意义,因为我可以“保存”聚集索引并释放一些空间来覆盖非聚集索引?

在此处输入图片说明

<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.1" Build="10.50.6000.34" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
  <BatchSequence>
    <Batch>
      <Statements>
        <StmtSimple StatementCompId="20" StatementEstRows="1.00059" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.0131459" StatementText="SELECT&#xD;&#xA;Object1.Column1 ,&#xD;&#xA;Object1.Column2,&#xD;&#xA;Object2.Column3,&#xD;&#xA; Object2.Column4 ,&#xD;&#xA; Object2.Column5 &#xD;&#xA;From Object1 &#xD;&#xA;LEFT JOIN&#xD;&#xA;Object2 ON Object2.Column2 = (SELECT TOP ? Column2 FROM Object2 WHERE Column6=Object1.Column2 AND Column7=Variable1)&#xD;&#xA;&#xD;&#xA;WHERE &#xD;&#xA;Object1.Column1  = ?&#xD;&#xA;AND Object1.Column8=Variable2 AND Object1.Column7=Variable1&#xD;&#xA;order by Object1.Column1 " StatementType="SELECT" QueryHash="0x48743CD92ACB6E34" QueryPlanHash="0xC3C3C48F42E94CC5">
          <StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
          <QueryPlan DegreeOfParallelism="1" CachedPlanSize="40" CompileTime="4" CompileCPU="4" CompileMemory="544">
            <RelOp AvgRowSize="72" EstimateCPU="4.18248E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1.00059" LogicalOp="Left Outer Join" NodeId="0" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="0.0131459">
              <OutputList>
                <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column2" />
                <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column1" />
                <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column5" />
                <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column4" />
                <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column3" />
              </OutputList>
              <RunTimeInformation>
                <RunTimeCountersPerThread Thread="0" ActualRows="1" ActualEndOfScans="1" ActualExecutions="1" />
              </RunTimeInformation>
              <NestedLoops Optimized="false">
                <OuterReferences>
                  <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column2" />
                </OuterReferences>
                <RelOp AvgRowSize="39" EstimateCPU="0.000158101" EstimateIO="0.003125" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1.00059" LogicalOp="Index Seek" NodeId="1" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0032831" TableCardinality="527433">
                  <OutputList>
                    <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column2" />
                    <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column1" />
                  </OutputList>
                  <RunTimeInformation>
                    <RunTimeCountersPerThread Thread="0" ActualRows="1" ActualEndOfScans="1" ActualExecutions="1" />
                  </RunTimeInformation>
                  <IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
                    <DefinedValues>
                      <DefinedValue>
                        <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column2" />
                      </DefinedValue>
                      <DefinedValue>
                        <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column1" />
                      </DefinedValue>
                    </DefinedValues>
                    <Object Database="Database1" Schema="Schema1" Table="Object1" Index="Index1" IndexKind="NonClustered" />
                    <SeekPredicates>
                      <SeekPredicateNew>
                        <SeekKeys>
                          <Prefix ScanType="EQ">
                            <RangeColumns>
                              <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column7" />
                              <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column8" />
                              <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column1" />
                            </RangeColumns>
                            <RangeExpressions>
                              <ScalarOperator ScalarString="ScalarString1">
                                <Identifier>
                                  <ColumnReference Column="Column9" />
                                </Identifier>
                              </ScalarOperator>
                              <ScalarOperator ScalarString="ScalarString2">
                                <Identifier>
                                  <ColumnReference Column="Column10" />
                                </Identifier>
                              </ScalarOperator>
                              <ScalarOperator ScalarString="ScalarString3">
                                <Const ConstValue="Value3" />
                              </ScalarOperator>
                            </RangeExpressions>
                          </Prefix>
                        </SeekKeys>
                      </SeekPredicateNew>
                    </SeekPredicates>
                  </IndexScan>
                </RelOp>
                <RelOp AvgRowSize="40" EstimateCPU="4.18E-06" EstimateIO="0" EstimateRebinds="0.000594258" EstimateRewinds="0" EstimateRows="1" LogicalOp="Inner Join" NodeId="2" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="0.00985859">
                  <OutputList>
                    <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column5" />
                    <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column4" />
                    <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column3" />
                  </OutputList>
                  <RunTimeInformation>
                    <RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="1" ActualExecutions="1" />
                  </RunTimeInformation>
                  <NestedLoops Optimized="false">
                    <OuterReferences>
                      <ColumnReference Column="Column11" />
                      <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column6" />
                    </OuterReferences>
                    <RelOp AvgRowSize="27" EstimateCPU="4.18E-06" EstimateIO="0" EstimateRebinds="0.000594258" EstimateRewinds="0" EstimateRows="1" LogicalOp="Inner Join" NodeId="3" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="0.00657121">
                      <OutputList>
                        <ColumnReference Column="Column11" />
                        <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column6" />
                      </OutputList>
                      <RunTimeInformation>
                        <RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="1" ActualExecutions="1" />
                      </RunTimeInformation>
                      <NestedLoops Optimized="false">
                        <OuterReferences>
                          <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column2" />
                        </OuterReferences>
                        <RelOp AvgRowSize="23" EstimateCPU="1E-07" EstimateIO="0" EstimateRebinds="0.000594258" EstimateRewinds="0" EstimateRows="1" LogicalOp="Top" NodeId="4" Parallel="false" PhysicalOp="Top" EstimatedTotalSubtreeCost="0.00328382">
                          <OutputList>
                            <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column2" />
                          </OutputList>
                          <RunTimeInformation>
                            <RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="1" ActualExecutions="1" />
                          </RunTimeInformation>
                          <Top RowCount="false" IsPercent="false" WithTies="false">
                            <TopExpression>
                              <ScalarOperator ScalarString="ScalarString4">
                                <Const ConstValue="Value4" />
                              </ScalarOperator>
                            </TopExpression>
                            <RelOp AvgRowSize="27" EstimateCPU="0.000158127" EstimateIO="0.003125" EstimateRebinds="0.000594258" EstimateRewinds="0" EstimateRows="1" LogicalOp="Clustered Index Seek" NodeId="5" Parallel="false" PhysicalOp="Clustered Index Seek" EstimatedTotalSubtreeCost="0.00328323" TableCardinality="837182">
                              <OutputList>
                                <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column2" />
                              </OutputList>
                              <RunTimeInformation>
                                <RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="1" ActualExecutions="1" />
                              </RunTimeInformation>
                              <IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
                                <DefinedValues>
                                  <DefinedValue>
                                    <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column2" />
                                  </DefinedValue>
                                </DefinedValues>
                                <Object Database="Database1" Schema="Schema1" Table="Object2" Index="Index2" TableReferenceId="2" IndexKind="Clustered" />
                                <SeekPredicates>
                                  <SeekPredicateNew>
                                    <SeekKeys>
                                      <Prefix ScanType="EQ">
                                        <RangeColumns>
                                          <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column6" />
                                        </RangeColumns>
                                        <RangeExpressions>
                                          <ScalarOperator ScalarString="ScalarString5">
                                            <Identifier>
                                              <ColumnReference Database="Database1" Schema="Schema1" Table="Object1" Column="Column2" />
                                            </Identifier>
                                          </ScalarOperator>
                                        </RangeExpressions>
                                      </Prefix>
                                    </SeekKeys>
                                  </SeekPredicateNew>
                                </SeekPredicates>
                                <Predicate>
                                  <ScalarOperator ScalarString="ScalarString6">
                                    <Compare CompareOp="EQ">
                                      <ScalarOperator>
                                        <Identifier>
                                          <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column7" />
                                        </Identifier>
                                      </ScalarOperator>
                                      <ScalarOperator>
                                        <Identifier>
                                          <ColumnReference Column="Column9" />
                                        </Identifier>
                                      </ScalarOperator>
                                    </Compare>
                                  </ScalarOperator>
                                </Predicate>
                              </IndexScan>
                            </RelOp>
                          </Top>
                        </RelOp>
                        <RelOp AvgRowSize="27" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="0.000594258" EstimateRewinds="0" EstimateRows="1" LogicalOp="Index Seek" NodeId="7" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0032832" TableCardinality="837182">
                          <OutputList>
                            <ColumnReference Column="Column11" />
                            <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column6" />
                          </OutputList>
                          <RunTimeInformation>
                            <RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="0" ActualExecutions="0" />
                          </RunTimeInformation>
                          <IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
                            <DefinedValues>
                              <DefinedValue>
                                <ColumnReference Column="Column11" />
                              </DefinedValue>
                              <DefinedValue>
                                <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column6" />
                              </DefinedValue>
                            </DefinedValues>
                            <Object Database="Database1" Schema="Schema1" Table="Object2" Index="Index3" TableReferenceId="1" IndexKind="NonClustered" />
                            <SeekPredicates>
                              <SeekPredicateNew>
                                <SeekKeys>
                                  <Prefix ScanType="EQ">
                                    <RangeColumns>
                                      <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column2" />
                                    </RangeColumns>
                                    <RangeExpressions>
                                      <ScalarOperator ScalarString="ScalarString7">
                                        <Identifier>
                                          <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column2" />
                                        </Identifier>
                                      </ScalarOperator>
                                    </RangeExpressions>
                                  </Prefix>
                                </SeekKeys>
                              </SeekPredicateNew>
                            </SeekPredicates>
                          </IndexScan>
                        </RelOp>
                      </NestedLoops>
                    </RelOp>
                    <RelOp AvgRowSize="40" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="0.000594258" EstimateRewinds="0" EstimateRows="1" LogicalOp="Clustered Index Seek" NodeId="9" Parallel="false" PhysicalOp="Clustered Index Seek" EstimatedTotalSubtreeCost="0.0032832" TableCardinality="837182">
                      <OutputList>
                        <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column5" />
                        <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column4" />
                        <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column3" />
                      </OutputList>
                      <RunTimeInformation>
                        <RunTimeCountersPerThread Thread="0" ActualRows="0" ActualEndOfScans="0" ActualExecutions="0" />
                      </RunTimeInformation>
                      <IndexScan Lookup="true" Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
                        <DefinedValues>
                          <DefinedValue>
                            <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column5" />
                          </DefinedValue>
                          <DefinedValue>
                            <ColumnReference Database="Database1" Schema="Schema1" Table="Object2" Column="Column4" />
                          </DefinedValue>
                          <DefinedValue>
                            <Colu

ype*_*eᵀᴹ 5

在没有更多信息的情况下,这更多是猜测,但根据我们所拥有的信息来判断:

  • 一个相当宽的表格(平均每页 1.3 到 4.0 行)
  • 缓慢的查询正在使用:
    • PWFID在加入条件下,
    • 两列TitleSITime在选择列表和
    • 没有其他任何地方柱(WHEREHAVING等)

然后覆盖非聚集索引(PWFID) INCLUDE (SITime, Title)可能会提高查询的效率,因为它将需要读取更窄的索引(并且不查找表,无论是聚集还是堆)。不知道会有多少改进,因为查询涉及连接 30 个表 - 并且索引也不会那么窄,包含 500 个字符的列。


关于将表转换为堆:

这让我想知道摆脱聚集索引并使这个表成为堆是否有意义,因为我可以“保存”聚集索引并释放一些空间来覆盖非聚集索引?

我认为这无关紧要,至少对于这个和类似的查询。它可能会改变/改进插入查询的行为(因为不需要维护聚集索引),但它也可能会降低其他依赖于从聚集索引中查找更多列的查询的性能。

而且您不会节省任何(或大量)空间。数据必须存储在某处,无论表是堆还是簇。


添加 NC 索引的变化要小得多,我不希望有任何副作用 - 除了在查询中想要使用它 - 但仍然需要进行测试。

删除聚集索引并将表转换为堆有效地改变了所有 NC 索引的结构并删除了(聚集的)索引,因此它可能对执行的许多操作/查询产生多种甚至更严重的影响,并且需要更多的测试.