SQL, почему SELECT COUNT (*), MIN (col), MAX (col) быстрее, чем SELECT MIN (col), MAX (col)

Мы видим огромную разницу между этими запросами.

Медленный запрос

SELECT MIN(col) AS Firstdate, MAX(col) AS Lastdate 
FROM table WHERE status = 'OK' AND fk = 4193

Таблица "таблица". Счетчик сканирований 2, логических чтений 2458969, физических чтений 0, упреждающих чтений 0, lob логических чтений 0, lob физических чтений 0, упреждающих чтений lob 0.

Время выполнения SQL Server: Процессорное время = 1966 мс, затраченное время = 1955 мс.

Быстрый запрос

SELECT count(*), MIN(col) AS Firstdate, MAX(col) AS Lastdate 
FROM table WHERE status = 'OK' AND fk = 4193

Таблица «таблица». Счетчик сканирования 1, логических чтений 5803, физических чтений 0, упреждающих чтений 0, логических чтений lob 0, физических чтений lob 0, упреждающих чтений lob 0.

Время выполнения SQL Server: Процессорное время = 0 мс, прошедшее время = 9 мс.

Вопрос

В чем причина огромной разницы в производительности между запросами?

Обновление Небольшое обновление, основанное на вопросах, заданных в виде комментариев:

Порядок выполнения или повторное выполнение ничего не меняет с точки зрения производительности. Никаких дополнительных параметров не используется, и (тестовая) база данных больше ничего не делает во время выполнения.

Медленный запрос

|--Nested Loops(Inner Join)
 |--Stream Aggregate(DEFINE:([Expr1003]=MIN([DBTest].[dbo].[table].[startdate])))
   |    |--Top(TOP EXPRESSION:((1)))
   |         |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1008]) WITH ORDERED PREFETCH)
   |              |--Index Scan(OBJECT:([DBTest].[dbo].[table].[startdate]), ORDERED FORWARD)
   |              |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]),  WHERE:([DBTest].[dbo].[table].[FK]=(5806) AND [DBTest].[dbo].[table].[status]<>'A') LOOKUP ORDERED FORWARD)
   |--Stream Aggregate(DEFINE:([Expr1004]=MAX([DBTest].[dbo].[table].[startdate])))
        |--Top(TOP EXPRESSION:((1)))
             |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1009]) WITH ORDERED PREFETCH)
                  |--Index Scan(OBJECT:([DBTest].[dbo].[table].[startdate]), ORDERED BACKWARD)
                  |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]),  WHERE:([DBTest].[dbo].[table].[FK]=(5806) AND [DBTest].[dbo].[table].[status]<>'A') LOOKUP ORDERED FORWARD)

Быстрый запрос

 |--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1012],0)))
   |--Stream Aggregate(DEFINE:([Expr1012]=Count(*), [Expr1004]=MIN([DBTest].[dbo].[table].[startdate]), [Expr1005]=MAX([DBTest].[dbo].[table].[startdate])))
        |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1011]) WITH UNORDERED PREFETCH)
             |--Index Seek(OBJECT:([DBTest].[dbo].[table].[FK]), SEEK:([DBTest].[dbo].[table].[FK]=(5806)) ORDERED FORWARD)
             |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]),  WHERE:([DBTest].[dbo].[table].[status]<'A' OR [DBTest].[dbo].[table].[status]>'A') LOOKUP ORDERED FORWARD)

The execution plan from SSMS

Ответ

Ответ, данный ниже Мартином Смитом, кажется, объясняет проблему. Сверхкороткая версия состоит в том, что анализатор запросов MS-SQL ошибочно использует план запроса в медленном запросе, который вызывает полное сканирование таблицы.

Добавление счетчика (*), подсказки запроса с (FORCESCAN) или комбинированного индекса для столбцов startdate, FK и статуса устраняет проблему с производительностью.

15
задан CodingBarfield 21 September 2011 в 11:20
поделиться