Встановити використання NOCOUNT ON


341

Натхненний цим питанням, де існують різні погляди на НАСТРОЙКА НАКРУТУ ...

Чи варто використовувати SET NOCOUNT ON для SQL Server? Якщо ні, то чому б ні?

Що робить редакція 6, 22 липня 2011 року

Він пригнічує повідомлення "xx рядків, на які постраждало" після будь-якого DML. Це набір результатів, і після надсилання клієнт повинен його обробити. Він крихітний, але вимірюваний (див. Відповіді нижче)

Для тригерів тощо клієнт отримає кілька "постраждалих рядків xx", і це спричиняє всілякі помилки для деяких ORM, MS Access, JPA тощо (див. Правки нижче)

Фон:

Загальновизнаною найкращою практикою (я думав, поки це питання) є використання SET NOCOUNT ONв тригерах і збережених процедурах в SQL Server. Ми використовуємо його скрізь, і швидкий google показує велику кількість MVP з SQL Server.

MSDN каже, що це може зламати .net SQLDataAdapter .

Тепер це означає для мене, що SQLDataAdapter обмежується гранично простою обробкою CRUD, оскільки він очікує, що повідомлення "n рядків, на які постраждали" збігається. Отже, я не можу використовувати:

  • ЯКЩО Є, щоб уникнути дублікатів (повідомлення не стосується рядків) Примітка: використовуйте обережно
  • Де не існує (менше рядків, ніж очікувалося)
  • Відфільтруйте банальні оновлення (наприклад, дані фактично не змінюються)
  • Чи будь-який доступ до таблиці раніше (наприклад, ведення журналу)
  • Приховати складність або денормізацію
  • тощо

У запитанні marc_s (хто знає його речі SQL) каже, що не використовуйте його. Це відрізняється від того, що я думаю (і я вважаю себе дещо компетентним в SQL).

Можливо, мені чогось не вистачає (сміливо зазначте очевидне), але що ви думаєте, хто там?

Примітка: минуло роки, як я побачив цю помилку, оскільки в даний час не використовую SQLDataAdapter.

Зміни після коментарів та запитань:

Редагувати: більше думок ...

У нас є кілька клієнтів: один може використовувати C # SQLDataAdaptor, інший може використовувати nHibernate від Java. На них можна впливати по-різному SET NOCOUNT ON.

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

Редагування 2: тригер, що порушує nібічне запитання , де SET NOCOUNT ONне можна встановити

(і ні, це не дублікат цього )

Edit 3: Ще більше інформації, завдяки моєму колезі з MVP

Редагування 4: 13 травня 2011 року

Порушує Linq 2 SQL також, коли не вказано?

Редагування 5: 14 червня 2011

Розбиває JPA, зберігається proc із змінними таблиці: Чи підтримує JPA 2.0 змінні таблиці SQL Server?

Редагування 6: 15 серпня 2011 року

Сітка даних SSMS "Редагувати рядки" вимагає SET NOCOUNT ON: тригер оновлення за допомогою GROUP BY

Редагувати 7: 07 березня 2013 року

Більш докладно про @RemusRusanu:
чи НАСТРОЮВАННЯ НОКУНТУ НА ВКЛЮЧЕННІ дійсно так багато різниці в продуктивності


@AlexKuznetsov: Яким би був підхід "Threadsafe"? Напевно, читання, виконані в EXISTS, все-таки будуть включати будь-яку непогашену транзакцію?
AnthonyWJones

2
@Jeremy Seghi: вибачте за пізню відповідь. Повідомлення (#rows постраждало) - це інструмент клієнтського інструменту, інтерпретований SSMS тощо: однак є пакет, надісланий з цією інформацією. Звичайно, я знаю, як працює @@ rowcount тощо, але це не питання ...
gbn

1
Не хвилюйтесь. Особисто я згоден з вашою позицією; Я просто коментував, що не існує прямої кореляції між результатами конструкції IF / WHERE EXITS та SET NOCOUNT. Я отримую стійкі результати від цих конструкцій незалежно від NOCOUNT. Якщо у вас є щось сказане інакше, будь ласка, надішліть це моїм шляхом.
Джеремі S

1
@ Jeremy Seghi: ви праві: SET NOCOUNT ON лише придушує додатковий пакет даних назад клієнту. ЯКЩО, @@ ROWCOUNT тощо не впливає. О, і це порушує SQLDataAdapters ... :-)
gbn

1
@Kieren Johnstone: заднім числом це питання погано сформульоване. Я б проголосував за закриття, якби це не було моїм питанням
gbn

Відповіді:


246

Гаразд, я провів своє дослідження, ось угода:

У протоколі TDS SET NOCOUNT ONзберігається лише 9 байт на запит, тоді як сам текст "ВКЛЮЧИТИ НОКУНТ" є колосальним 14 байтом. Я думав, що 123 row(s) affectedповернуто з сервера простим текстом в окремому мережевому пакеті, але це не так. Це насправді невелика структура, яка називається DONE_IN_PROCвбудованою у відповідь. Це не окремий мережевий пакет, тому не витрачаються туди і назад.

Я думаю, ви можете завжди дотримуватися поведінки підрахунку за замовчуванням майже завжди, не турбуючись про продуктивність. Однак є деякі випадки, коли попереднє обчислення кількості рядків вплине на продуктивність, наприклад, курсор, спрямований лише вперед. У такому випадку NOCOUNT може бути необхідністю. Крім цього, абсолютно не потрібно дотримуватися девізу "використовувати NOCOUNT, де це можливо".

Ось дуже детальний аналіз незначущості SET NOCOUNTналаштувань: http://daleburnett.com/2014/01/everything-ever-wanted-know-set-nocount/


Справді. Я використовую SET NOCOUNT ON назавжди, але marc_s вказав на обмеження SQLDataAdapter в іншому питанні.
gbn

Дякую. Байти чи розмір не є проблемою для мене, але клієнт повинен його обробити. Ця залежність від SQLDataAdapter все ще мене вражає ...
gbn

2
Дякую за вашу відповідь. Я прийму це через ваші розслідування, які викликали більше інформації та роботи у мене. Я не погоджуюсь із загальними словами: це може мати значення, як показують інші відповіді. Ура, gbn
gbn

13
Це не кількість байтів, а затримка в зворотному шляху через провід, яка є вбивцею
racingsnail

1
У потоці повідомлень TDS, наприклад, коли в таблицю вставляють лише значення. Повідомлення DONINPROC(RPC) або DONE(BATCH) передається rowcountу відповідні рядки, а done_countпрапор - trueнезалежно від того, чи NO_COUNTє ON. Залежить від реалізації конвертації клієнта у випадках, коли запит містить оператори SELECT або виклики RPC, які не вибираються, може знадобитися відключити підрахунок ... КОЛИ відключений, рядки все ще зараховуються для оператора select, але прапор DONE_COUNTвстановлений у false. Завжди читайте, що пропонує ваш клієнт-ліб, оскільки він буде інтерпретувати потік токена (повідомлення) замість вас
Мілан Ярич

87

Мені знадобилося багато копати, щоб знайти справжні цифри орієнтиру навколо NOCOUNT, тому я подумав, що поділюсь коротким підсумком.

  • Якщо ваша збережена процедура використовує курсор для виконання багатьох дуже швидких операцій без повернутих результатів, NOCOUNT OFF може зайняти приблизно 10 разів, ніж увімкнути її. 1 Це найгірший сценарій.
  • Якщо ваша збережена процедура виконує лише одну швидку операцію без повернутих результатів, встановлення NOCOUNT ON може призвести до збільшення продуктивності на 3%. 2 Це буде відповідати типовій процедурі вставки або оновлення. (Див. Коментарі до цієї відповіді, щоб ознайомитись із тим, чому це не завжди може бути швидшим.)
  • Якщо ваша збережена процедура повертає результати (тобто ви вибираєте щось), різниця в продуктивності зменшиться пропорційно розміру набору результатів.

5
+1 за впливом на курсор, це відповідає моїм спостереженням
zvolkov

Пункт 2 не точний! Я маю на увазі блог, на який він посилається. Ніколи не було! DONE, DONEPROC і DONEINPROC надсилаються однакового розміру, незалежно від того, чи для NO_COUNT встановлено значення ON або OFF. RowCount все ще існує як ULONGLONG (64 байти), а прапор DONE_COUNT все ще є, але бітове значення 0. SQL-сервер все одно буде рахувати кількість рядків, навіть якщо вам не цікаво читати значення з токена DONE. Якщо ви читаєте @@ ROWCOUNT, то ви додали більше байтів у потік токенів або у вигляді маркера returnvalue, або у вигляді іншого colmetadata + лексеми рядків!
Мілан Ярич

@MilanJaric: Дякую, що зателефонували. Ви допомогли мені зрозуміти, що я пов’язав неправильну статтю. Посилання оновлюється зараз, і стаття надає вагомий аргумент, щоб показати, що може бути незначне поліпшення продуктивності при встановленні НАЗАД НОКУНТ. Як ви вважаєте, чи є проблеми з методами орієнтиру, які використовуються?
StriplingWarrior

:) досі неточна щодо НАСТРОЙКА НОКУНТУ ВКЛЮЧЕНО / ВКЛ. Помилка в тому, що другого SP немає, SET NOCOUNT OFF;і тому вони думають, що вони не отримують зайвих байтів у відповідь. Точним орієнтиром буде використання SET NOCOUNT ONв лівій і SET NOCOUNT OFFправій збереженій процедурі. Таким чином, ви отримаєте TDS-пакет з DONEINPROC (SET NOCOUNT ...), ще раз десять DONEINPROC (INSERT statement), а потім RETURNVALUE(@@ROWCOUNT), потім RETURNSTATUS 0для sp та finaly DONPROC. Помилка існує через те, що в другому списку не встановлено вимкнено НОКУНТ!
Мілан Ярич

Щоб перефразовувати те, що вони знайшли, але вони не усвідомили, що якщо у вас є запит курсору 1K вибору, спочатку зробіть один запит, щоб встановити NOCOUNT на УВІМКНЕНО або ВИКЛ. Фактичний стан підключення для NOCOUNT ON або OFF не вплине на пропускну здатність, це може просто заплутати вкладку клієнта, наприклад ADO.net або ODBC. Тому "не використовуйте SET NOCOUNT <WHATEVER>, якщо ви дбаєте про пропускну здатність" :)
Milan Jaric

77
  • Якщо SET NOCOUNT увімкнено, підрахунок (із зазначенням кількості рядків, на які впливає оператор Transact-SQL), не повертається. Якщо SET NOCOUNT вимкнено, підрахунок повертається. Він використовується з будь-яким оператором SELECT, INSERT, UPDATE, DELETE.

  • Установка SET NOCOUNT встановлюється під час виконання або запуску, а не в час розбору.

  • SET NOCOUNT ON (ВКЛ. НАКР.) Покращує продуктивність збереженої процедури (SP).

  • Синтаксис: SET NOCOUNT {ON | OFF}

Приклад SET NOCOUNT ON:

введіть тут опис зображення

Приклад SET SET NOCOUNT OFF:

введіть тут опис зображення


7
Легко та швидко зрозуміти за допомогою скріншотів. Чудова робота. :)
shaijut

35

Я певною мірою думаю, це проблема DBA проти розробника.

Як розробник, я б сказав, що не використовуйте його, якщо ви абсолютно не позитивно ставитесь до цього, оскільки його використання може порушити ваш код ADO.NET (як це підтверджено в Microsoft).

І я думаю, що як DBA, ви будете більше з іншого боку - використовуйте його, коли це можливо, якщо ви справді не повинні перешкодити його використанню.

Крім того, якщо ваші розробники коли-небудь використовують "RecordsAffected", що повертається за ExecuteNonQueryдопомогою виклику методу ADO.NET , у вас виникнуть проблеми, якщо всі використовують, SET NOCOUNT ONоскільки в цьому випадку ExecuteNonQuery завжди поверне 0.

Також дивіться допис у блозі Пітера Бромберга та ознайомтеся з його позицією.

Тож справді зводиться до того, хто має встановлювати стандарти :-)

Марк


Хоча він про простий CRUD: сітка даних, яку він згадує, може використовувати xml для надсилання декількох рядків, щоб уникнути туди і назад
gbn

Я думаю, якщо ви ніколи не використовуєте SqlDataAdapters, і ви ніколи не перевіряєте і не покладаєтесь на число "зафіксованих записів", повернене ExecuteNonQuery (наприклад, якщо ви використовуєте щось на зразок Linq-S-SQL або NHibernate), то, ймовірно, у вас немає проблем використовуючи SET NOCOUNT ON у всіх збережених документах.
marc_s

12

Якщо ви говорите, що у вас можуть бути і різні клієнти, є проблеми з класичним ADO, якщо SET NOCOUNT не встановлено.

Одне з яких я регулярно відчуваю: якщо збережена процедура виконує декілька висловлювань (і, таким чином, повертається кількість повідомлень "xxx рядків, які постраждали"), ADO, здається, не справляється з цим і видає помилку "Неможливо змінити властивість ActiveConnection об'єкта Recordset який має джерело Command. "

Тому я загалом виступаю за його ввімкнення, якщо немає дійсно поважних причин цього не робити. ви, можливо, знайшли дійсно справжню причину, яку мені потрібно переглянути і прочитати докладніше.


9

Загрожуючи ускладненням, я закликаю трохи відрізнятись від правил, які я бачу вище:

  • Завжди встановлюйте NOCOUNT ONу верхній частині облікового запису, перш ніж виконувати будь-яку роботу в процедурі, але також завжди SET NOCOUNT OFFзнову, перш ніж повертати будь-які набори записів із збереженого програму.

Отже, "як правило, не вмикайте рахунок, за винятком випадків, коли ви фактично повертаєте набір результатів". Я не знаю жодних способів, завдяки яким це може зламати будь-який клієнтський код, це означає, що клієнтському коду ніколи не потрібно нічого знати про внутрішні протоколи, і це не особливо важко.


Дякую. Ви, звичайно, можете отримати кількість рядків з DataSet або споживчого контейнера, але це може бути корисно. Ми не можемо мати тригери на SELECT, щоб це було безпечно: більшість помилок клієнта викликаються помилковими повідомленнями про зміни даних.
gbn

Проблема з цим правилом полягає лише в тому, що його важче перевірити, ніж "Чи ВКЛЮЧЕНО НАЛАШТУВАННЯ у верхній частині процедури?"; Цікаво, чи можуть інструменти аналізу SQL, такі як Sql Enlight, перевірити подібні речі ... Додавання його до мого довгострокового списку todo для мого проекту форматування SQL :)
Дао,

5

Щодо тригерів, що розбивають NHibernate, я мав такий досвід з перших рук. В основному, коли NH робить ОНОВЛЕННЯ, він очікує певної кількості рядків, на які впливає. Додавши SET NOCOUNT ON у тригери, ви отримаєте кількість рядків назад до того, що очікував NH, тим самим виправити проблему. Так що так, я б точно рекомендував вимкнути його для спрацьовування, якщо ви використовуєте NH.

Що стосується використання в СП, то це питання особистої переваги. Я завжди відключав відрахування рядків, але знову ж таки, справжніх сильних аргументів немає.

З іншого боку, ви дійсно повинні розглянути можливість відійти від архітектури на базі SP, тоді у вас навіть цього питання не виникне.


1
Я не згоден з відстороненням від збережених програм. Це означає, що ми повинні мати один і той же SQL у двох різних кодах клієнта і довіряти нашим клієнтським кодерам. Ми DBA розробника. І чи не означає «SET NOCOUNT ON »?
gbn

@CodeBlend: просто Google це більше, ніж коли-небудь потрібно. Однак ... stackoverflow.com/a/4040466/27535
ГБН

3

Я хотів переконатися в тому, що "SET NOCOUNT ON" не зберігає мережевий пакет, ані зворотній перехід

Я використовував тестовий SQLServer 2017 на іншому хості (я використовував VM). create table ttable1 (n int); insert into ttable1 values (1),(2),(3),(4),(5),(6),(7) go create procedure procNoCount as begin set nocount on update ttable1 set n=10-n end create procedure procNormal as begin update ttable1 set n=10-n end Потім я простежив пакети на порту 1433 за допомогою інструмента "Wireshark": кнопка "фільтр захоплення" -> "порт 1433"

exec procNoCount

це пакет відповідей: 0000 00 50 56 c0 00 08 00 0c 29 31 3f 75 08 00 45 00 0010 00 42 d0 ce 40 00 40 06 84 0d c0 a8 32 88 c0 a8 0020 32 01 05 99 fe a5 91 49 e5 9c be fb 85 01 50 18 0030 02 b4 e6 0e 00 00 04 01 00 1a 00 35 01 00 79 00 0040 00 00 00 fe 00 00 e0 00 00 00 00 00 00 00 00 00

exec procNormal

це пакет відповідей: 0000 00 50 56 c0 00 08 00 0c 29 31 3f 75 08 00 45 00 0010 00 4f d0 ea 40 00 40 06 83 e4 c0 a8 32 88 c0 a8 0020 32 01 05 99 fe a5 91 49 e8 b1 be fb 8a 35 50 18 0030 03 02 e6 1b 00 00 04 01 00 27 00 35 01 00 ff 11 0040 00 c5 00 07 00 00 00 00 00 00 00 79 00 00 00 00 0050 fe 00 00 e0 00 00 00 00 00 00 00 00 00

У рядку 40 я бачу "07", яка є числом "рядків, на які впливає". Він входить у пакет відповідей. Немає зайвих пакетів.

Однак у ньому є 13 додаткових байтів, які можна зберегти, але, мабуть, не варто, ніж зменшити назви стовпців (наприклад, "ManagingDepartment" до "MD")

Тому я не бачу причин використовувати його для виконання

Але як згадували інші, це може зламати ADO.NET, і я також натрапив на проблему, використовуючи python: MSSQL2008 - Pyodbc - Попередній SQL не був запитом

Так що, мабуть, добра звичка все-таки ...


1
SET NOCOUNT ON;

Цей рядок коду використовується в SQL для неповернення рядків чисел, на які впливає виконання запиту. Якщо нам не потрібна кількість порушених рядків, ми можемо використовувати це, оскільки це допоможе в економії використання пам'яті та збільшить швидкість виконання запиту.


2
Зауважте, що все ще встановлено @@ ROWCOUNT. SET NOCOUNT ON придушує будь-яку додаткову відповідь, яку SQL Server надсилає клієнту. Дивіться прийняту відповідь вище, будь ласка
gnn

1

ВКЛЮЧИТИ НОКУНТ; Вищевказаний код зупинить повідомлення, що генерується двигуном sql-сервера, до віконця результатів після виконання команди DML / DDL.

Чому ми це робимо? Оскільки двигун SQL-сервера вимагає певного ресурсу для отримання статусу та генерування повідомлення, це вважається перевантаженим для сервера двигуна Sql. Отже, ми встановлюємо повідомлення про безрахунок.


1

Одне місце, яке SET NOCOUNT ONдійсно може допомогти, - це те, де ви робите запити в циклі чи курсорі. Це може скласти велику кількість мережевого трафіку.

CREATE PROCEDURE NoCountOn
AS
set nocount on
    DECLARE @num INT = 10000
    while @num > 0
    begin
       update MyTable SET SomeColumn=SomeColumn
       set @num = @num - 1
    end
GO


CREATE PROCEDURE NoCountOff
AS
set nocount off
    DECLARE @num INT = 10000
    while @num > 0
    begin
       update MyTable SET SomeColumn=SomeColumn
       set @num = @num - 1
    end
GO

Якщо ввімкнути статистику клієнтів у SSMS, пробіг EXEC NoCountOnта EXEC NoCountOffпоказує, що на NoCountOff був додатковий трафік 390 Кб:

статистика клієнтів

Напевно, не ідеально робити запити в циклі чи курсорі, але ми теж не живемо в ідеальному світі :)


0

Я знаю, це досить старе питання. але лише для оновлення.

Найкращий спосіб використовувати "SET NOCOUNT ON" - це поставити його як перше твердження у вашому SP та встановити його знову перед останнім оператором SELECT.


0

SET NOCOUNT ON навіть робить доступ до таких рядків, як це:

SET NOCOUNT ON

DECLARE @test TABLE (ID int)

INSERT INTO @test
VALUES (1),(2),(3)

DECLARE @affectedRows int = -99  

DELETE top (1)
  FROM @test
SET @affectedRows = @@rowcount

SELECT @affectedRows as affectedRows

Результати

постраждалі ряди

1

Повідомлення

Команди успішно виконані.

Час закінчення: 2020-06-18T16: 20: 16.9686874 + 02: 00


-1

Я не знаю, як перевірити SET NOCOUNT ON між клієнтом та SQL, тому я перевірив подібну поведінку для іншої команди SET "НАСТРОЙКА ІЗОЛЯЦІЇ ІЗОЛЯЦІЇ ТРАНЗАЦІЇ ПРОЧИТАЙТЕ НЕЗАКОНОДЕНО"

Я надіслав команду зі свого з'єднання, змінивши поведінку SQL за замовчуванням (ПРОЧИТАТИ ЗВ'ЯЗАНО), і це було змінено для наступних команд. Коли я змінив рівень ISOLATION всередині збереженої процедури, це не змінило поведінку з'єднання для наступної команди.

Поточний висновок,

  1. Зміна параметрів всередині збереженої процедури не змінює налаштування за замовчуванням для з'єднання.
  2. Зміна налаштування шляхом надсилання команд за допомогою ADOCOnnection змінює поведінку за замовчуванням.

Я думаю, що це стосується інших команд SET, таких як "SET NOCOUNT ON"


Чи означає ваш пункт 1 вище, що вам дійсно не потрібно ВІДКЛЮЧАТИ НОКУНТ в кінці, оскільки це не впливає на глобальне середовище?
funkymushroom

Я не впевнений, чи це він мав на увазі під пунктом 1, але в моїх тестах так, мабуть, на глобальне середовище не впливає SET NOCOUNT ON всередині збереженої процедури.
Дуг

Це був поганий вибір порівняння, оскільки рівень ізоляції явно пов'язаний з певною транзакцією, тому немає жодної конкретної причини очікувати, що вона відповідає такому параметру, якNOCOUNT
IMSoP

-1

якщо (встановити без рахунку == вимкнено)

{тоді він зберігатиме дані про кількість записів, на які впливає, та зменшує продуктивність} інше {він не відстежує запис змін, отже покращить ефективність}}


-1

Іноді навіть найпростіші речі можуть змінити значення. Один з цих простих елементів, який повинен бути частиною кожної збереженої процедури, - це SET NOCOUNT ON. Цей рядок коду, покладений у верхню частину збереженої процедури, вимикає повідомлення, які SQL Server надсилає назад клієнту після виконання кожного оператора T-SQL. Це виконується для всіх SELECT, INSERT, UPDATEі DELETEзаяви. Наявність цієї інформації зручно, коли ви запускаєте оператор T-SQL у вікні запиту, але коли запускаються збережені процедури, немає необхідності передавати цю інформацію клієнту.

Видаляючи цей додатковий накладні витрати з мережі, це може значно покращити загальну продуктивність вашої бази даних та додатків.

Якщо вам все-таки потрібно отримати кількість рядків, на які впливає оператор T-SQL, який виконується, ви все одно можете скористатися @@ROWCOUNTпараметром. Видавши SET NOCOUNT ONцю функцію ( @@ROWCOUNT), вона все ще працює і її все ще можна використовувати у ваших збережених процедурах для визначення кількості рядків, на які впливає оператор.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.