为什么MAX在索引视图上的表现比TOP差得多?

时间:2010-12-20 03:14:04

标签: sql tsql sql-server-2008

我发现,在具有适当索引的索引视图上,MAX(日期)执行整个索引扫描,然后执行流聚合,而TOP(1)日期最佳地使用索引并且仅扫描单个行。对于大量行,这会导致严重的性能问题。我已经包含了一些代码来演示下面的问题,但有兴趣知道其他人是否可以解释为什么会发生这种行为(它不会发生在具有类似索引的表上)以及它是否是SQL Server优化器中的错误(I已经在2008 SP2和R2上进行了测试,两者都显示了相同的问题。)

CREATE TABLE dbo.TableWithDate
(
  id INT IDENTITY(1,1) PRIMARY KEY,
  theDate DATE NOT NULL
);

CREATE NONCLUSTERED INDEX [ix_date] ON dbo.TableWithDate([theDate] DESC);

INSERT INTO dbo.TableWithDate(theDate) VALUES('1 MAR 2010'),('1 MAR 2010'), ('3 JUN 2008');

-- Test 1:  max vs top(1) on the table.  They give same optimal plan (scan one row from the index, since index is in order)
SELECT TOP(1) theDate FROM dbo.TableWithDate ORDER BY theDate DESC;
SELECT MAX(theDate) FROM dbo.TableWithDate;

CREATE TABLE dbo.TheJoinTable
(
  identId INT IDENTITY(1,1) PRIMARY KEY,
  foreignId INT NOT NULL,
  someValue INT NOT NULL
);

CREATE NONCLUSTERED INDEX [ix_foreignValue] ON dbo.TheJoinTable([foreignId] ASC);

INSERT INTO dbo.TheJoinTable(foreignId,someValue) VALUES (1,10),(1,20),(1,30),(2,5),(3,6),(3,10);

GO

CREATE VIEW dbo.TheTablesJoined 
WITH SCHEMABINDING
AS 
  SELECT T2.identId, T1.id, T1.theDate, T2.someValue
  FROM dbo.TableWithDate AS T1
  INNER JOIN dbo.TheJoinTable AS T2 ON T2.foreignId=T1.id
GO

-- Notice the different plans:  the TOP one does a scan of 1 row from each and joins
-- The max one does a scan of the entire index and then does seek operations for each item (less efficient)
SELECT TOP(1) theDate FROM dbo.TheTablesJoined ORDER BY theDate DESC;

SELECT MAX(theDate) FROM dbo.TheTablesJoined;

-- But what about if we put an index on the view?  Does that make a difference?
CREATE UNIQUE CLUSTERED INDEX [ix_clust1] ON dbo.TheTablesJoined([identId] ASC);
CREATE NONCLUSTERED INDEX [ix_dateDesc] ON dbo.TheTablesJoined ([theDate] DESC);

-- No!!!! We are still scanning the entire index (look at the actual number of rows) in the MAX case.
SELECT TOP(1) theDate FROM dbo.TheTablesJoined ORDER BY theDate DESC;

SELECT MAX(theDate) FROM dbo.TheTablesJoined;

3 个答案:

答案 0 :(得分:2)

John Sansom covered MAXTOP的表现特征,但他的结果并没有具体回答你的问题。

我认为答案在于MAX是一个通用聚合函数,用于处理页面和数据页面,其中TOP是一个仅限制行数的运算符牵强。

在这个狭窄的用例中,两个示例查询都能够执行相同的操作,并且可以返回相同的结果。使用TOP的查询受益于使用该方法为此用例提供的特定优化。

我删除了两个查询的XML计划,而使用MAX的语句包含:

<DefinedValues>
  <DefinedValue>
    <ColumnReference Column="Expr1004" />
    <ScalarOperator ScalarString="MAX([db].[dbo].[TheTablesJoined].[theDate])">
      <Aggregate AggType="MAX" Distinct="false">
        <ScalarOperator>
          <Identifier>
            <ColumnReference Database="[db]" Schema="[dbo]" Table="[TheTablesJoined]" Column="theDate" />
          </Identifier>
        </ScalarOperator>
      </Aggregate>
    </ScalarOperator>
  </DefinedValue>
</DefinedValues>

使用TOP的语句包含此代替定义MAX查询中聚合内容的XML:

<TopExpression>
  <ScalarOperator ScalarString="(1)">
    <Const ConstValue="(1)" />
  </ScalarOperator>
</TopExpression>

使用TOP时执行计划中的事情要少得多。

答案 1 :(得分:1)

要评估任何聚合函数(如max)的值,必须读取表中的所有行,并且因为在评估中使用了其中一个值。 前1只需要读一行,这可以很快完成 当它不是按顺序强制而没有适合的索引扫描整个表时。 在这些情况下,您可以创建合适的索引来提高性能。

答案 2 :(得分:0)

什么版本的SQL Server?只有Enterprise和Developer会自动使用索引视图,其他版本会扩展查询以反对基础表。

您想指定NOEXPAND查询提示。请参阅How can i speed up this Indexed View?

的答案