Почему MAX работает намного хуже, чем TOP в индексированном представлении? - PullRequest
6 голосов
/ 20 декабря 2010

Я обнаружил, что в индексированном представлении с соответствующими индексами MAX (дата) выполняет полное сканирование индекса, за которым следует агрегат потока, тогда как дата TOP (1) оптимально использует индекс и сканирует только одну строку.Для большого количества строк это приводит к серьезным проблемам с производительностью.Я включил некоторый код, чтобы продемонстрировать проблему ниже, но было бы интересно узнать, могут ли другие объяснить, почему происходит такое поведение (оно не встречается в таблице с аналогичным индексом) и является ли это ошибкой в ​​оптимизаторе SQL Server (япротестированы как на 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 ]

2 голосов
/ 25 декабря 2010

Джон Сансом покрыл характеристики производительности MAX против TOP, однако его результаты не дали конкретного ответа на ваш вопрос.

Я думаю, что ответ заключается в том факте, что 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, содержал это вместо XML, определяющего, что агрегируется в запросе MAX:

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

При использовании TOP.

в плане выполнения намного меньше
1 голос
/ 20 декабря 2010

Чтобы оценить значение любой агрегатной функции, такой как max, все строки в таблице должны быть прочитаны, и потому что в оценке используется одно из ее значенийТоп 1 должен прочитать только одну строку, это можно сделать очень быстро, если это не вызвано порядком и нет подходящего индекса для сканирования всей таблицы.В этих случаях вы можете создать подходящий индекс для повышения производительности.

0 голосов
/ 25 декабря 2010

Какая редакция SQL Server? Только Enterprise и Developer будут использовать индексированные представления автоматически, другие выпуски расширят запрос, чтобы соответствовать базовым таблицам.

Вы хотите указать подсказку запроса NOEXPAND. См. Ответ для Как я могу ускорить это индексированное представление?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...