0

此问题类似,但给出的建议似乎不适用于我的情况

我有一个看起来像这样的#temp 表:

  CREATE TABLE #C (
    OutA int,
    OutB int,
    OutC int,
    SortD float,
    PartE nvarchar(400),
    PartF nvarchar(400)
  )

现在它包含大约。10M 行,尽管我需要它来处理比这更多的行(我已经限制了它,以便检查查询计划不会花一整天的时间!)

该表具有以下索引和统计信息:

  CREATE INDEX B ON #C(SortD DESC) INCLUDE (OutA, OutB, OutC)
  CREATE INDEX C ON #C(PartE, SortD DESC) INCLUDE (OutA, OutB, OutC)
  CREATE INDEX D ON #C(PartF, SortD DESC) INCLUDE (OutA, OutB, OutC)

  CREATE STATISTICS E ON #C (OutA);  
  CREATE STATISTICS F ON #C (OutB);  
  CREATE STATISTICS G ON #C (OutC);  

(我不确定为什么需要统计数据,这似乎对计划没有影响,但是当它们丢失时它会抱怨)

最后,我尝试使用不同的分区但相同的排序顺序在此数据上创建 3 个不同的排名

  SELECT      OutA,
              OutB,
              OutC,
              RANK() OVER (ORDER BY SortD DESC) AS [Rank1],
              RANK() OVER (PARTITION BY PartE ORDER BY SortD DESC) AS [Rank2],
              RANK() OVER (PARTITION BY PartF ORDER BY SortD DESC) AS [Rank3]
  INTO        #Junk1
  FROM        #C

这会产生这个实际的查询计划 实际查询计划

这需要将近 2 分钟才能运行。

如您所见,此计划中有多种昂贵的种类。根据链接的问题,我创建的索引应该可以在这里使用,但它们没有被使用。

如果我改为创建 3 个单独的查询,每个等级 1 个,在这种情况下,索引将按预期使用。我确实尝试利用它并加入 3 个查询结果以产生相同的输出,但这实际上总体上花费了稍微长一点的时间

我还尝试修改索引以包含分区列,如下所示:

  CREATE INDEX B ON #C(SortD DESC) INCLUDE (OutA, OutB, OutC, PartE, PartF)
  CREATE INDEX C ON #C(PartE, SortD DESC) INCLUDE (OutA, OutB, OutC, PartF)
  CREATE INDEX D ON #C(PartF, SortD DESC) INCLUDE (OutA, OutB, OutC, PartE)

通过从表扫描更改为索引扫描(B),这成功地删除了第一个(最右边的)排序。但其他种类仍然存在

为什么需要多个排名时不使用索引?我怎样才能消除昂贵的种类?

4

1 回答 1

0

不是真正的原因答案,但我确实找到了解决方法。

而不是创建所有 3 个索引,然后尝试一次完成所有 3 个排名。如果我们改为分步执行,一次添加 1 个索引 + 1 个排名,那么它确实会使用索引。

(注意这和生成3个排名然后加入是不一样的)

  CREATE INDEX B ON #C(SortD DESC) INCLUDE (OutA, OutB, OutC, PartE, PartF)

  CREATE STATISTICS E ON #C (OutA);   -- not sure if
  CREATE STATISTICS F ON #C (OutB);   -- these statistics
  CREATE STATISTICS G ON #C (OutC);   -- are still needed

  SELECT  OutA,
          OutB, 
          OutC,
          PartE,
          PartF,
          SortD,
          RANK() OVER (ORDER BY SortD DESC) AS [Rank1]
  INTO    #Intermediate1
  FROM    #C

  CREATE INDEX C ON #Intermediate1(PartE, SortD DESC) INCLUDE (OutA, OutB, OutC, PartF, Rank1) -- Need to include Rank1 here for this index to be useable

  SELECT  OutA,
          OutB,
          OutC,
          Rank1,
          PartF,
          SortD,
          RANK() OVER (PARTITION BY PartE ORDER BY SortD DESC) AS [Rank2]
  INTO    #Intermediate2
  FROM    #Intermediate1

-- could drop #Intermediate1 at this point

  CREATE INDEX D ON #Intermediate2(PartF, SortD DESC) INCLUDE (OutA, OutB, OutC, Rank1, Rank2) --PartE no longer required
  SELECT  OutA,
          OutB,
          OutC,
          Rank1,
          Rank2,
          RANK() OVER (PARTITION BY PartF ORDER BY SortD DESC) AS [Rank3]
  FROM    #Intermediate2

注意这样做可能需要更多的临时空间 - 可能需要权衡。

我认为将 Rank1 和 Rank2 列包含到索引中的要求可能表明为什么一次性完成所有 3 列不使用索引。检查排序,我们可以看到 [Expr*] 作为输出字段包含:表达式- 我认为这是之前排名的输出。

这对我来说感觉像是应该在 SQL 的优化器中修复的东西?

于 2021-04-08T12:45:11.687 回答