Фон
Я заметил, что при экспериментировании с запущенными полными запросами, что иногда оценочный план просто показывает "запрос на выборку"
и фактический план показывает повторные выборки из сканирования кластерного индекса
в других случаях (например, при добавлении TOP
к запросу) в оценочном плане показан этап "Население", который заполняет рабочую таблицу
С фактическим планом, показывающим сканирование кластерного индекса, чтобы заполнить рабочую таблицу, повторное обращение к этой рабочей таблице.
Вопрос
- Какие критерии использует SQL Server при выборе одного подхода над другим?
- Я был бы прав, думая, что первый метод (без дополнительного шага популяции рабочей таблицы) более эффективен?
(Бонусный вопрос: если кто-нибудь может объяснить, почему каждое сканирование в первом запросе считается 2-мя логическими чтениями, которые также могут быть достаточно просвещенными)
Дополнительная информация
Я нашел эту статью здесь, в которой объясняется, что курсоры FAST_FORWARD
могут использовать динамический план или статический план. Первый запрос в этом случае, по-видимому, использует динамический план, а второй - статический план.
Я также обнаружил, что если я попробую
SET @C2 = CURSOR DYNAMIC TYPE_WARNING FOR SELECT TOP ...
Курсор получает неявное преобразование в курсор keyset
, поэтому ясно, что конструкция TOP
не поддерживается для динамических курсоров, возможно, по причинам, указанным в ответе Рубена. Все еще ищет окончательное объяснение этого.
Однако я также читал, что динамические курсоры имеют тенденцию медленнее, чем их статические аналоги (источник 1, источник 2), что кажется мне удивительным, учитывая, что статический сорт должен читать исходные данные, копировать его, а затем читать копию, а не просто читать источник данных. В статье, на которую я ссылался ранее, упоминается, что динамические курсоры используют markers
. Кто-нибудь может объяснить, что это такое? Это просто код RID или ключ CI или что-то другое?
Script
SET STATISTICS IO OFF
CREATE TABLE #T ( ord INT IDENTITY PRIMARY KEY, total INT, Filler char(8000))
INSERT INTO #T (total) VALUES (37),(80),(55),(31),(53)
DECLARE @running_total INT,
@ord INT,
@total INT
SET @running_total = 0
SET STATISTICS IO ON
DECLARE @C1 AS CURSOR;
SET @C1 = CURSOR FAST_FORWARD FOR SELECT ord, total FROM #T ORDER BY ord;
OPEN @C1;
PRINT 'Initial FETCH C1'
FETCH NEXT FROM @C1 INTO @ord, @total ;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @running_total = @running_total + @total
PRINT 'FETCH C1'
FETCH NEXT FROM @C1 INTO @ord, @total ;
END
SET @running_total = 0
SET STATISTICS IO ON
DECLARE @C2 AS CURSOR;
SET @C2 = CURSOR FAST_FORWARD FOR SELECT TOP 5 ord, total FROM #T ORDER BY ord;
OPEN @C2;
PRINT 'Initial FETCH C2'
FETCH NEXT FROM @C2 INTO @ord, @total ;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @running_total = @running_total + @total
PRINT 'FETCH C2'
FETCH NEXT FROM @C2 INTO @ord, @total ;
END
PRINT 'End C2'
DROP TABLE #T