Когда у курсора FAST_FORWARD будет рабочая таблица (и этого следует избегать)? - PullRequest
31 голосов
/ 24 октября 2011

Справочная информация

Я заметил, что во время экспериментов с запуском итоговых запросов иногда в оценочном плане просто отображается «Запрос на выборку»

Fetch

, а на самом деле отображается план.повторные выборки из сканирования кластеризованного индекса

Fetch Scan

в других случаях (например, при добавлении TOP к запросу) в оценочном плане отображается этап «Запроса населения», который заполняетрабочий стол

Fetch and Populate

С фактическим планом, показывающим сканирование кластерного индекса для заполнения рабочей таблицы, затем повторяется поиск по этой рабочей таблице.

Seeks

Вопрос

  1. Какие критерии использует SQL Server при выборе одного подхода вместо другого?
  2. Буду ли я прав, полагая, что первый метод (без дополнительной рабочей таблицы)шаг заполнения) более эффективен?

(Бонусный вопрос: если кто-нибудь может объяснить, почему каждое сканирование в первом запросе считается за 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 

Ответы [ 2 ]

9 голосов
/ 26 октября 2011

Просто догадка, но обычно для TOP-ORDER BY требуется, чтобы SQL Server каким-то образом буферизовал результат (либо результат сканирования индекса, либо действительно весь результат во временной структуре, или что-то среднее).

Можно утверждать, что для курсоров это также необходимо даже при упорядочении по первичному ключу (как в вашем примере), поскольку вы не можете позволить курсору TOP 5 неожиданно возвращать менее 5 строк, когда соответствующий SELECT делаетвозвращает ровно 5 строк (или хуже: курсор возвращает более 5 строк).

Теоретически, эта странная ситуация может возникнуть, если в таблице есть удаления или вставки после диапазон сканирования индексадля курсора уже определено, и вставки / удаления попадают в диапазон сканирования индекса, но вы еще не закончили извлечение.Чтобы этого не случилось, они могут ошибиться в безопасности.(И они просто не оптимизировали для таблиц #temp.)

Вопрос, однако: SQL Server допускает FETCH FROM SELECT TOP n без предложения ORDER BY?(Здесь не запущен экземпляр SQL Server.) Может быть, интересно узнать, какой план это вызывает.

5 голосов
/ 21 ноября 2014

Какие критерии использует SQL Server при выборе одного подхода вместо другого?

Это прежде всего решение, основанное на затратах. Цитата из статьи, на которую вы ссылались, «В ситуациях, когда динамический план выглядит многообещающим, сравнение затрат может быть эвристически пропущено. Это происходит в основном для чрезвычайно дешевых запросов, хотя детали эзотеричны».

Буду ли я прав, думая, что первый метод (без шага заполнения дополнительного рабочего стола) более эффективен?

Это зависит. Динамические и статические планы курсоров имеют разные сильные и слабые стороны. Если в конечном итоге будут затронуты все строки, статический план, вероятно, будет работать лучше. Подробнее об этом чуть позже.

Понятно, что конструкция TOP не поддерживается для динамических курсоров

Это правда. Все итераторы в плане динамического курсора должны иметь возможность сохранять и восстанавливать состояние, сканировать вперед и назад, обрабатывать одну входную строку для каждой выходной строки и быть неблокирующими. Верх, в общем, не удовлетворяет всем этим требованиям; класс CQScanTopNew не реализует необходимые методы Set/Get/Goto/Marker() и ReverseDirection() (среди прочих).

Я также читал, что динамические курсоры, как правило, медленнее, чем их статические аналоги.

Это часто верно для курсоров Transact-SQL, где затрагивается большая часть или весь набор курсоров. С сохранением и восстановлением состояния динамического плана запросов связаны затраты. Если при каждом вызове обрабатывается одна строка, а все строки в конце концов затрагиваются, это увеличивает затраты на сохранение / восстановление.

Статические курсоры накладывают издержки на создание копии набора (который может быть доминирующим фактором для большого набора), но стоимость извлечения для каждой строки довольно мала. Наборы ключей имеют более высокие издержки на поиск в строке, чем статические, поскольку они должны внешне присоединяться к исходным таблицам для получения неключевых столбцов.

Динамические курсоры оптимальны, когда к относительно небольшой части набора обращаются, и / или поиск не является строкой за раз. Это типичный шаблон доступа во многих распространенных сценариях курсора, только те, которые не тестируются в блогах:)

Если кто-нибудь может объяснить, почему каждое сканирование в первом запросе считается за 2 логических чтения, что также может быть очень полезным

Это относится к тому, как сохраняется состояние сканирования и как считываются показания.

В статье, на которую я ссылался ранее, упоминается, что динамические курсоры используют маркеры. Кто-нибудь может объяснить, что это? Это просто RID или CI, или что-то другое?

Для динамического плана курсора существуют маркеры для каждого итератора, а не только методы доступа. «Маркер» - это вся информация о состоянии, необходимая для перезапуска итератора плана в том месте, где он остановился. Для метода доступа большая часть этого - RID или индексный ключ (при необходимости, с помощью унивификатора), но никак не вся история.

...