ROWCOUNT TOP

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Рассматривая планы запроса для INSERT, UPDATE или DELETE, в том числе те, которые демонстрировались в некоторых статьях ранее, можно заметить, что почти все такие планы включают оператора TOP. Например, следующий ниже сценарий с оператором UPDATE создает показательный для демонстрации этого план:

CREATE TABLE T (A INT) 
INSERT T VALUES (0) 
INSERT T VALUES (1) 
INSERT T VALUES (2)

UPDATE T SET A = A + 1
Rows  Executes
3      1      UPDATE [T] set [A] = [A]+@1
3      1        |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))
0      0             |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+[@1]))
3      1                  |--Top(ROWCOUNT est 0)
3      1                       |--Table Scan(OBJECT:([T]))

Что TOP делает прямо после просмотра таблицы?

Это ROWCOUNT TOP. Он используется для реализации функциональности SET ROWCOUNT. "est 0" указывает на то что, когда запрос был скомпилирован, SET ROWCOUNT был равен нулю («est» — это сокращение от «estimate», хотя это значение во время компиляции не влияет на оптимизацию или выполнение запроса). Отметим, что значение 0 означает выборку или обновление всех строк. Поскольку во время выполнения SET ROWCOUNT также был равен 0, возвращаемый STATISTICS PROFILE результат, показывает, что все 3 строки были обновлены.

Теперь попробуйте следующее:

SET ROWCOUNT 1
UPDATE T SET A = A + 1
Rows  Executes
1      1      UPDATE [T] set [A] = [A]+@1
1      1        |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))
0      0             |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+[@1]))
1      1                  |--Top(ROWCOUNT est 0)
1      1                       |--Table Scan(OBJECT:([T]))

Хотя мы получаем тот же план (включая ROWCOUNT TOP с той же «estimate»), в этот раз SET ROWCOUNT был равен 1, поэтому при просмотре таблицы TOP вернул только одну строку, и потому была обновлена только одна строка.

Если сделать рекомпиляцию, то значение «estimate» изменяется:

SET ROWCOUNT 1
UPDATE T SET A = A + 1 OPTION (RECOMPILE)
Rows  Executes
1      1      UPDATE T SET A = A + 1 OPTION (RECOMPILE)
1      1        |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))
0      0             |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+(1)))
1      1                  |--Top(ROWCOUNT est 1)
1      1                       |--Table Scan(OBJECT:([T]))

Почему SQL Server не добавляет ROWCOUNT TOP к операторам выборки?

Например, следующий план запроса не содержит TOP, но возвращает только одну строку:

SET ROWCOUNT 1
SELECT * FROM T
Rows  Executes
1      1      SELECT * FROM T
1      1        |--Table Scan(OBJECT:([T]))

SQL Server реализует SET ROWCOUNT для операторов SELECT простым подсчётом, выбирая указанное количество строк из корня плана. Хотя это может работать и для тривиального плана с UPDATE, наподобие того, который был показан выше, это не будет работать для более сложных планов с UPDATE. Например, если мы добавим в нашу таблицу уникальный индекс, план с UPDATE станет существенно сложнее:

CREATE UNIQUE INDEX TA ON T(A) 
UPDATE T SET A = A + 1
Rows  Executes
2      1      UPDATE [T] set [A] = [A]+@1
2      1        |--Index Update(OBJECT:([T].[TA]), SET:([Bmk10061024] = [Bmk1006],[A1025] = [T].[A]))
2      1             |--Collapse(GROUP BY:([T].[A]))
2      1                  |--Filter(WHERE:(NOT [Expr1021]))
2      1                       |--Sort(ORDER BY:([T].[A] ASC, [Act1023] ASC))
2      1                            |--Split
1      1                                 |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))
1      1                                      |--Compute Scalar(DEFINE:([Expr1021]=[Expr1021]))
0      0                                           |--Compute Scalar(DEFINE:([Expr1021]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))
0      0                                                |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+(1), [Expr1005]=CASE WHEN [T].[A] = ([T].[A]+(1)) THEN (1) ELSE (0) END))
1      1                                                     |--Top(ROWCOUNT est 1)
1      1                                                          |--Table Scan(OBJECT:([T]))

Мы не будем в этой статье разбирать все детали последнего плана. Оставим это для последующих статей. Однако обратите внимание, что при изменении одной строки, корень этого плана возвращает две строки. Если бы было отсчитана одна строка от корня плана, это не дало бы правильного результата. Поместив ROWCOUNT TOP над просмотром таблицы, оптимизатор может гарантировать, что сервер обновит правильное количество строк независимо от сложности остальной части плана.

Источник: https://habr.com/ru/post/690228/


Интересные статьи

Интересные статьи

Привет! На связи Данила Соловьев, руководитель направления PHP в AGIMA. Для проджект-менеджеров и джуниор-разработчиков я подготовил небольшой гайд по тому, как ускорять работу крупных проектов на Бит...
Говоря о разработке сайтов с использованием CMS 1C Bitrix вопрос покрытия тестами поднимается редко. Главная причина в том, что большинство проектов обходится штатным функционалом, который предоставля...
Одна из самых важных (на мой взгляд) функций в Битрикс24 это бизнес-процессы. Теоретически они позволяют вам полностью избавиться от бумажных служебок и перенести их в эл...
Устраивать конкурсы в инстаграме сейчас модно. И удобно. Инстаграм предоставляет достаточно обширный API, который позволяет делать практически всё, что может сделать обычный пользователь ручками.
В 1С Битрикс есть специальные сущности под названием “Информационные блоки, сокращенно (инфоблоки)“, я думаю каждый с ними знаком, но не каждый понимает, что это такое и для чего они нужны