Мы видим огромную разницу между этими запросами.
Медленный запрос
SELECT MIN(col) AS Firstdate, MAX(col) AS Lastdate
FROM table WHERE status = 'OK' AND fk = 4193
Таблица 'table'. Число сканирования 2, логическое чтение 2458969, физическое считывание 0, чтение вперед 0, логическое чтение логических чисел 0, физическое чтение lob 0, чтение с чтением lob 0.
Время выполнения SQL Server: Время CPU = 1966 мс, прошедшее время = 1955 мс.
Быстрый запрос
SELECT count(*), MIN(col) AS Firstdate, MAX(col) AS Lastdate
FROM table WHERE status = 'OK' AND fk = 4193
Таблица 'table'. Число сканирования 1, логическое считывание 5803, физическое чтение 0, чтение вперед 0, логическое чтение логических чисел 0, физическое чтение lob 0, чтение с чтением lob 0.
Время выполнения SQL Server: Время CPU = 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)
Ответ
Ответ, приведенный ниже, Мартин Смит, кажется, объясняет проблему. Супер короткая версия заключается в том, что анализатор запросов MS-SQL ошибочно использует план запроса в медленном запросе, который вызывает полное сканирование таблицы.
Добавление графа (*), подсказка запроса с (FORCESCAN) или комбинированный индекс в столбцах startdate, FK и status устраняет проблему с производительностью.