Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 ExecuteNonQuery() и SET NOCOUNT ON  [new]
KellyLynch
Member

Откуда:
Сообщений: 94
У меня есть:
MS SQL Server 2008 R2;
C# приложение – его клиент

Я встретил следующий (причём невоспроизводимый регулярно) баг. Такой вот код на клиенте приносит “количество затронутых строк” большее чем их должно быть на самом деле:

…
DbCommand cmd = db.GetStoredProcCommand("[b]TheStoredProc[/b]");
int numberOfAffectedRows = db.ExecuteNonQuery(cmd);
…
Сама процедура TheStoredProc в базе выглядит примерно так:

PROCEDURE TheStoredProc 
as

	declare @Var1 int
	
	[color=blue]SET NOCOUNT ON;[/color]

....................

[b]insert into [Workflows][/b]
	(
		WorkflowInstanceID,
		Status
	)
	select WorkflowInstanceID, 1
		from #tmp
		

	DROP TABLE #tmp


Процедуру писал не я; но из контекста следует что Клиент ожидает что в результате вызова ExecuteNonQuery ему вернётся в numberOfAffectedRows количество строк вставленных процедурой вот в этом операторе INSERT – “insert into [Workflows] …”.

Причём – только этим insert-ом. В процедуре чуть выше есть ещё один insert; но по контексту очевидно что Клиенту нужно именно и только количество строк вставленных оператором “insert into [Workflows]…”

Баг состоит в том что в результате выполнения этой процедуры кодом Клиента в его переменной numberOfAffectedRows оказалось число 464; а судя по содержимому базы, должно было быть только 419. Всё усложняется тем что баг “трудноповторимый” – его заметили раз на Production SQL Server-е; а вот повторить его на своём тестовом мне пока не удалось – возвращается правильное количество строк в numberOfAffectedRows.

Моя версия такова: виновником бага я считаю оператор “SET NOCOUNT ON” в начале процедуры TheStoredProc. Вот здесь [url=]https://social.msdn.microsoft.com/Forums/en-US/e85ad5e5-a30e-4ccf-aeab-0774185e7576/set-nocount-on-and-sqlcommandexecutenonquery-and-sqldataadapterupdate?forum=adodotnetdataproviders[/url] говорится:

автор

I have heard few side effects of using "SET NOCOUNT ON"
a. SQLCommand.ExecuteNonQuery function returning wrong number of rows if the stored procedure has SET NOCOUNT ON.



“wrong number of rows” – как раз то что у меня. Правда, на форумах в других подобных случаях я чаще всего встречал такие сообщения: “в случае использования SET NOCOUNT ON в процедуре метод ExecuteNonQuery возвращает -1”. У меня же, как видите, не -1 а “несколько большее число, чем правильное”.

Других версий у меня пока нет.

Что вы об этом думаете? Правдоподобна моя версия? Так как баг я пока ни разу не воспроизвёл в тестовой среде, мне лишь остаётся оценивать правдоподобность версий…

Исправить баг я предполагаю вставив в процедуре “SET NOCOUNT OFF” прямо перед “insert into [Workflows]…”. На тестовом сервере попробовал – всё выходит правильно. Но и (как я сказал) старый вариант на тестовом сервере тоже работает правильно…
16 апр 15, 18:06    [17525820]     Ответить | Цитировать Сообщить модератору
 Re: ExecuteNonQuery() и SET NOCOUNT ON  [new]
Glory
Member

Откуда:
Сообщений: 104751
KellyLynch
I have heard few side effects of using "SET NOCOUNT ON"

Ага. ОБС - одна бабка сказала.
Ниже цитируют офф.хелп
SET NOCOUNT ON prevents the sending of DONE_IN_PROC messages to the client for each statement in a stored procedure.
Что такое DONE_IN_PROC вы можете узнать здесь https://msdn.microsoft.com/en-us/library/dd340553.aspx
17 апр 15, 08:19    [17527236]     Ответить | Цитировать Сообщить модератору
 Re: ExecuteNonQuery() и SET NOCOUNT ON  [new]
KellyLynch
Member

Откуда:
Сообщений: 94
Glory
KellyLynch
I have heard few side effects of using "SET NOCOUNT ON"

Ага. ОБС - одна бабка сказала.
Ниже цитируют офф.хелп
SET NOCOUNT ON prevents the sending of DONE_IN_PROC messages to the client for each statement in a stored procedure.
Что такое DONE_IN_PROC вы можете узнать здесь https://msdn.microsoft.com/en-us/library/dd340553.aspx


Исходя из этого Вы считаете невозможным описанный выше сценарий? Когда включение SET NOCOUNT ON привело к тому что ExecuteNonQuery() стал возвращать больше строк чем должен?
Если почитать официальное описание DONE_IN_PROC, то получается: установка в процедуре SET NOCOUNT ON может привести к тому что ExecuteNonQuery() возвратит меньше строк чем должен. В моём же случае он возвратил больше строк чем должен...

Согласны с моими рассуждениями? Или я что-то упустил?
17 апр 15, 10:32    [17527888]     Ответить | Цитировать Сообщить модератору
 Re: ExecuteNonQuery() и SET NOCOUNT ON  [new]
Glory
Member

Откуда:
Сообщений: 104751
KellyLynch
Когда включение SET NOCOUNT ON привело к тому что ExecuteNonQuery() стал возвращать больше строк чем должен?

Должно кому ? Где еще, кроме вашего неизвестного клиентского кода, проверялось что должно возвращаться и что возвращается ?

KellyLynch
Если почитать официальное описание DONE_IN_PROC, то получается: установка в процедуре SET NOCOUNT ON может привести к тому что ExecuteNonQuery() возвратит меньше строк чем должен. В моём же случае он возвратил больше строк чем должен...

А где написано, что скрывется за вашим многоточием между SET NOCOUNT ON и insert into [Workflows] ?
17 апр 15, 10:37    [17527929]     Ответить | Цитировать Сообщить модератору
 Re: ExecuteNonQuery() и SET NOCOUNT ON  [new]
felix_ff
Member

Откуда: Moscow
Сообщений: 1698
Glory
KellyLynch
Когда включение SET NOCOUNT ON привело к тому что ExecuteNonQuery() стал возвращать больше строк чем должен?

Должно кому ? Где еще, кроме вашего неизвестного клиентского кода, проверялось что должно возвращаться и что возвращается ?

KellyLynch
Если почитать официальное описание DONE_IN_PROC, то получается: установка в процедуре SET NOCOUNT ON может привести к тому что ExecuteNonQuery() возвратит меньше строк чем должен. В моём же случае он возвратил больше строк чем должен...

А где написано, что скрывется за вашим многоточием между SET NOCOUNT ON и insert into [Workflows] ?


PROCEDURE TheStoredProc 
   @rows INT = NULL OUTPUT
as

	declare @Var1 int
	
	[color=blue]SET NOCOUNT ON;[/color]

....................

[b]insert into [Workflows][/b]
	(
		WorkflowInstanceID,
		Status
	)
	select WorkflowInstanceID, 1
		from #tmp
		
     SET @rows = @@ROWCOUNT
	DROP TABLE #tmp


DbCommand cmd = db.GetStoredProcCommand("[b]TheStoredProc[/b]");
db.ExecuteNonQuery(cmd);
int numberOfAffectedRows = cmd.Parameters("@rows").Value;

ну а если так?
17 апр 15, 13:37    [17529153]     Ответить | Цитировать Сообщить модератору
 Re: ExecuteNonQuery() и SET NOCOUNT ON  [new]
invm
Member

Откуда: Москва
Сообщений: 9646
KellyLynch
Я встретил следующий (причём невоспроизводимый регулярно) баг.
Знакомая история: получив результат, отличный от ожидаемого, разработчик сразу считает это багом продукта, а не свой ошибкой :)

1. В режиме nocount on ExecuteNonQuery будет возвращать -1. Если это не так, значит часть DML в пакете выполнялись таки с nocount off;
2. Внимательно прочитав статью документации по ExecuteNonQuery, можно понять, что использовать результат этой функции для определения количества обработанных строк можно только в простейших случаях, да и то впоследствии можно получить сюрпризы.
17 апр 15, 13:44    [17529218]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить