为什么SQL Server会选择Clustered Index Scan over Non-Clustered?

时间:2011-06-09 07:21:14

标签: sql-server

在我查询的其中一个表中,在一个不是主键的键上创建了聚簇索引。 (我不知道为什么。)

但是,此表的主键有一个非聚集索引。

在执行计划中,SQL正在选择聚簇索引,而不是我在查询中使用的主键的非聚集索引。

SQL有没有理由这样做?如何强制SQL选择非聚集索引?


附加更多细节:

该表有许多字段,查询包含许多连接。让我抽象一下。

表定义如下所示:

SlowTable
[SlowTable_id] [int] IDENTITY(200000000,1) NOT NULL,
[fk1Field] [int] NULL,
[fk2Field] [int] NULL,
[other1Field] [varchar] NULL,
etc. etc...

然后该表的索引是:

fk1Field (Clustered)
SlowTable_id (Non-Unique, Non-Clustered)
fk2Field (Non-Unique, Non-Clustered)
... and 14 other Non-Unique, Non-Clustered indices on other fields

据推测,针对fk1Field的查询数量更多,这就是为什么他们选择此作为聚集索引的基础。

我的查询使用了一个视图:

SELECT
  [field list]
FROM 
     SourceTable1 S1
     INNER JOIN SourceTable2 S2
       ON S2.S2_id = S1.S2_id
     INNER JOIN SourceTable3 S3
       ON S3.S3_id = S2.S3_id
     INNER JOIN SlowTable ST
       ON ST.SlowTable_id = S1.SlowTable_id
     INNER JOIN [many other tables, around 7 more...]

执行计划非常大,有关节点说

Hash Match 
(Inner Join)
Cost: 9%

带有指向

的粗箭头
Clustered Index Scan (Clustered)
SlowTable.fk1Field 
Cost: 77%

我希望这能提供足够详细的问题。

谢谢!


附录2: 更正我以前的帖子。视图没有where子句。它只是一系列内部联接。执行计划取自Insert语句,该语句在复杂查询中使用View(列为SLOW_VIEW),如下所示:

(这个存储过程的作用是根据权重对某些记录的总量进行按比例分割,计算为总数的百分比。这模仿了从一个帐户向其他帐户分配值。 )

INSERT INTO dbo.WDTD(
    FieldA,
    FieldB,
    GWB_id,
    C_id,
    FieldC,
    PG_id,
    FieldD,
    FieldE,
    O_id,
    FieldF,
    FieldG,
    FieldH,
    FieldI,
    GWBIH_id,
    T_id,
    JO_id,
    PC_id,
    PP_id,
    FieldJ,
    FieldK,
    FieldL,
    FieldM,
    FieldN,
    FieldO,
    FieldP,
    FieldQ,
    FieldS)
SELECT DISTINCT
    @FieldA FieldA,
    GETDATE() FieldB,
    @Parameter1 GWB_id,
    GWBIH.C_id C_id,
    P.FieldT FieldC,
    P.PG_id PG_id,
    PAM.FieldD FieldD,
    PP.FieldU FieldE,
    GWBIH.O_id O_id,
    CO.FieldF FieldF,
    CO.FieldG FieldG,
    PSAM.FieldH FieldH,
    PSAM.FieldI FieldI,
    SOURCE.GWBIH_id GWBIH_id,
    ' ' T_id,
    GWBIH.JO_id JO_id,
    SOURCE.PC_id PC_id,
    GWB.PP_id,
    SOURCE.FieldJ FieldJ,
    1 FieldK,
    ROUND((SUM(GWBIH.Total) / AGG.Total) * SOURCE.Total, 2) FieldL,
    ROUND((SUM(GWBIH.Total) / AGG.Total) * SOURCE.Total, 2) FieldM,
    0 FieldN,
    ' ' FieldO,
    ESGM.FieldP_flag FieldP,
    SOURCE.FieldQ FieldQ,
     '[UNPROCESSED]'
FROM
    dbo.Table1 GWBIH
    INNER JOIN dbo.Table2 GWBPH
        ON GWBPH.GWBP_id = GWBIH.GWBP_id
    INNER JOIN dbo.Table3 GWB
        ON GWB.GWB_id = GWBPH.GWB_id
    INNER JOIN dbo.Table4 P
        ON P.P_id = GWBPH.P_id
    INNER JOIN dbo.Table5 ESGM
        ON ESGM.ET_id = P.ET_id
    INNER JOIN dbo.Table6 PAM
        ON PAM.PG_id = P.PG_id
    INNER JOIN dbo.Table7 O
        ON O.dboffcode = GWBIH.O_id
    INNER JOIN dbo.Table8 CO
        ON
            CO.Country_id = O.Country_id
            AND CO.Brand_id = O.Brand_id
    INNER JOIN dbo.Table9 PSAM
        ON PSAM.Office_id = GWBIH.O_id
    INNER JOIN dbo.Table10 PCM
        ON PCM.PC_id = GWBIH.PC_id
    INNER JOIN dbo.Table11 PC
        ON PC.PC_id = GWBIH.PC_id
    INNER JOIN dbo.Table12 PP
        ON PP.PP_id = GWB.PP_id
            -- THIS IS THE VIEW THAT CONTAINS THE CLUSTERED INDEX SCAN
    INNER JOIN dbo.SLOW_VIEW GL
        ON GL.JO_id = GWBIH.JO_id
    INNER JOIN (
        SELECT
            GWBIH.C_id C_id,
            GWBPH.GWB_id,
            SUM(GWBIH.Total) Total
        FROM
            dbo.Table1 GWBIH
            INNER JOIN dbo.Table2 GWBPH
                ON GWBPH.GWBP_id = GWBIH.GWBP_id
            INNER JOIN dbo.Table10 PCM
                ON PCM.PC_id = GWBIH.PC_id
        WHERE
            PCM.Split_flag = 0
            AND GWBIH.JO_id IS NOT NULL
        GROUP BY
            GWBIH.C_id,
            GWBPH.GWB_id
            ) AGG
        ON AGG.C_id = GWBIH.C_id
            AND AGG.GWB_id = GWBPH.GWB_id
    INNER JOIN (
        SELECT
            GWBIH.GWBIH_id GWBIH_id,
            GWBIH.C_id C_id,
            GWBIH.FieldQ FieldQ,
            GWBP.GWB_id GWB_id,
            PCM.PC_id PC_id,
            CASE
            WHEN WT.FieldS IS NOT NULL
                THEN WT.FieldS
            WHEN WT.FieldS IS NULL
                THEN PCMS.FieldT
            END FieldJ,
            SUM(GWBIH.Total) Total
        FROM
            dbo.Table1 GWBIH
            INNER JOIN dbo.Table2 GWBP
                ON GWBP.GWBP_id = GWBIH.GWBP_id
            INNER JOIN dbo.Table4 P
                ON P.P_id = GWBP.P_id
            INNER JOIN dbo.Table10 PCM
                ON PCM.PC_id = GWBIH.PC_id
            INNER JOIN dbo.Table11 PCMS
                ON PCMS.PC_id = PCM.PC_id
            LEFT JOIN dbo.WT WT
                ON WT.ET_id = P.ET_id
                AND WT.PC_id = GWBIH.PC_id
        WHERE
            PCM.Split_flag = 1
        GROUP BY
            GWBIH.GWBI_id,
            GWBIH.C_id,
            GWBIH.FieldQ,
            GWBP.GWB_id,
            WT.FieldS,
            PCM.PC_id,
            PCMS.ImportCode
            ) SOURCE
        ON SOURCE.C_id = GWBIH.C_id
            AND SOURCE.GWB_id = GWBPH.GWB_id
WHERE
    PCM.Split_flag = 0
    AND AGG.Total > 0
    AND GWBPH.GWB_id = @Parameter1
    AND NOT EXISTS (
        SELECT *
        FROM dbo.WDTD
        WHERE
            TD.C_id = GWBIH.C_id
            AND TD.FieldA = GWBPH.GWB_id
            AND TD.JO_id = GWBIH.JO_id
            AND TD.PC_id = SOURCE.PC_id
            AND TD.GWBIH_id = ' ')
GROUP BY
    GWBIH.C_id,
    P.FieldT,
    GWBIH.JO_id,
    GWBIH.O_id,
    GWBPH.GWB_id,
    P.PG_id,
    PAM.FieldD,
    PP.FieldU,
    GWBIH.O_id,
    CO.FieldF,
    CO.FieldG,
    PSAM.FieldH,
    PSAM.FieldI,
    GWBIH.JO_id,
    SOURCE.PC_id,
    GWB.PP_id,
    SOURCE.FieldJ,
    ESGM.FieldP_flag,
    SOURCE.GWBIH_id,
    SOURCE.FieldQ,
    AGG.Total,
    SOURCE.Total

ADDENDUM 3:在视图的select语句上执行执行计划时,我看到了:

Hash Match     <====   Bitmap           <------ etc...
(Inner Join)           (Bitmap Create)
Cost: 0%               Cost: 0%
    ^
    |
    |
Parallelism                           Clustered Index Scan (Clustered)
(Repartition Streams)    <====        Slow_Table.fk1Field
Cost: 1%                              Cost: 98%

附录4:我想我发现了这个问题。聚集索引扫描不是指引用主键的my子句,而是指另一个需要以某种方式与上面的fk1Field相关的字段的子句。

4 个答案:

答案 0 :(得分:4)

最有可能的一个:

  • 太多行以使索引生效
  • index不符合ON / WHERE条件
  • 索引未覆盖,SQL Server避免了密钥查找

更新后编辑:

您的索引是无用的,因为它们都是单列索引,因此它执行聚簇索引扫描。

您需要一个与ON,WHERE,GROUP BY条件匹配的索引以及SELECT列表的INCLUDES。

答案 1 :(得分:1)

如果您正在执行的查询没有选择一小部分记录,SQL Server可能会选择忽略任何“其他有用的”非聚集索引,只扫描聚簇索引(在本例中,大多数情况下)可能是表中的所有行) - 逻辑是执行查询所需的I / O量与完全扫描所需的非聚集索引权重相比。

如果您可以发布表格的架构+示例查询,我相信我们可以提供更多信息。

答案 2 :(得分:1)

理想情况下,您不应该告诉SQL Server这样做,或者如果您给它一个好的查询,它可以选择最好的。创建了查询提示以引导引擎,但您不必使用它。

有时以不同的方式对表进行聚类是有益的,主键很少见,但它很有用(聚类控制数据布局,而主键确保正确)。

我可以告诉你为什么SQL Server会选择聚集索引,如果你向我展示你的查询和架构,否则我只会猜测可能的原因,执行计划在这些情况下是有帮助的。

对于要考虑的非聚集索引,它必须对查询有意义,如果非聚集索引不包括您的查询,则根本没有保证它将被使用。

答案 3 :(得分:1)

聚簇索引扫描本质上是一种表扫描(在恰好具有聚簇索引的表上)。你真的应该发表你的陈述以获得更好的答案。您的where子句可能无法搜索(请参阅sargs),或者如果您选择了许多记录,sql server可能会扫描表而不是使用索引,以后必须查找相关列。