Область переменных, которые определены в блоке while в хранимых процедурах — сервер SQl

Я столкнулся с интересным сценарием (по крайней мере, для меня) в хранимой процедуре. Хотелось бы услышать мнение специалистов и мысли по этому поводу.

DECLARE @loopcounter INT
SET @loopcounter=10

WHILE @loopcounter > 0
BEGIN
  DECLARE @insidevalue int

  IF (@loopcounter%2 = 0)
  SET @insidevalue = @loopcounter

  PRINT 'Value_' + CAST(@insidevalue AS NVARCHAR) + '_'

  SET @loopcounter = @loopcounter - 1
END

Я ожидал, что этот блок выдаст результат, как показано ниже.

Value_10_

Value_ _

Value_8_

Value_ _

Value_6_

Value_ _

Value_4_

Value_ _

Value_2_

Value_ _

Вместо этого я получил вывод, как показано ниже:

Значение_10_

Значение_10_

Значение_8_

Значение_8_

Значение_6_

Значение_6_

Значение_4_

Значение_4_

Значение

Value_10_

Value_ _

Value_8_

Value_ _

Value_6_

Value_ _

Value_4_

Value_ _

Value_2_

Value_ _

Значение

Value_10_

Value_ _

Value_8_

Value_ _

Value_6_

Value_ _

Value_4_

Value_ _

Value_2_

Value_ _

Я думал, что если я объявлю переменную внутри блока while, то для каждой итерации она будет сбрасывать значение на NULL или значение по умолчанию (из фона С#).

Если это задумано, то мой вопрос заключается в том, как SQLServer обрабатывает оператор DECLARE для этой переменной внутри блока while? Игнорирует ли он это, поскольку переменная уже находится в памяти?

Может кто-нибудь объяснить мне это поведение?


person JPReddy    schedule 18.05.2011    source источник


Ответы (7)


Область действия переменной — это вся партия, в данном случае — хранимая процедура.

Он не объявляется повторно в каждом цикле

Так что это именно так, как ожидалось

Редактировать:

Есть недавняя статья в блоге, которая очень похожа. Автор быстро поправился :-)

person gbn    schedule 18.05.2011
comment
Вы имеете в виду, что он игнорирует объявление объявления в следующей итерации? - person JPReddy; 18.05.2011
comment
Если хотите так выразиться, то да. Но это не игнорируется, потому что это не имеет значения. SQL не имеет области действия, как вы ожидаете. - person gbn; 18.05.2011
comment
У меня есть привычка вручную сбрасывать переменные, которые я объявляю в циклах T-SQL, чтобы предотвратить такое поведение. Это действительно странно, если вы привыкли работать на таком языке, как C#. - person John Bledsoe; 18.05.2011
comment
@John: Даже у меня теперь будет эта привычка, раньше меня это никогда не заботило. - person JPReddy; 18.05.2011

Из Переменные Transact-SQL

Область действия переменной — это диапазон инструкций Transact-SQL, которые могут ссылаться на эту переменную. Область действия переменной длится с момента ее объявления до конца пакета или хранимой процедуры, в которой она объявлена.

DECLARE сам по себе не является исполняемым оператором. Все объявления переменных идентифицируются во время компиляции, а память зарезервирована для них в контексте выполнения.

Если вы используете синтаксис Declare и Set версии 2008+. Однако часть оператора Set будет выполняться при каждой итерации цикла.

DECLARE @loopcounter INT
SET @loopcounter=10

WHILE @loopcounter > 0
BEGIN
  DECLARE @insidevalue INT = NULL

  IF (@loopcounter%2 = 0)
  SET @insidevalue = @loopcounter

  PRINT 'Value_' + CAST(@insidevalue AS NVARCHAR) + '_'

  SET @loopcounter = @loopcounter - 1
END
person Martin Smith    schedule 18.05.2011

Попробуйте это для удовольствия

if 1 = 0
begin
  -- will never happen
  declare @xx int
end  
else  
begin
  set @xx = 1
end  
print @xx

По-видимому, код объявления не должен выполняться. Объявляться только до того, как он будет использован.

это не работает

if 1 = 0
begin
  -- will never happen
  set @xx = 1
end  
else  
begin
  declare @xx int
end  
print @xx
person Mikael Eriksson    schedule 18.05.2011
comment
+1 Интересно, если он не «выполнен», он также не подберет инициализатор, например. declare @xx int = 999 - person Alex K.; 18.05.2011

Из Объявить:

Областью действия локальной переменной является пакет, в котором она объявлена.

В T-SQL больше нет «локальных» правил области видимости. Это также означает, что вы не можете объявить одно и то же имя переменной внутри блоков IF и ELSE.

Все, что делает Declare, — это объявляет переменную. Это не имеет отношения к назначению. Значение любой переменной, которой никогда не было присвоено значение, равно NULL. Но после этого единственный способ, которым значение переменной снова станет NULL, — это явное присваивание.

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

person Damien_The_Unbeliever    schedule 18.05.2011

В T-SQL WHILE..END не имеет индивидуальной области действия, вы можете, например, SELECT @insidevalue после WHILE END.

person Alex K.    schedule 18.05.2011

Целочисленные типы данных часто не имеют NULL только 0

Заявление Declare не будет происходить каждый раз в цикле

почему бы тебе просто не использовать

DECLARE @loopcounter INT
SET @loopcounter=10
WHILE @loopcounter > 0
BEGIN
  IF @loopcounter%2 = 0
  PRINT 'Value_' + CAST(@loopcounter AS NVARCHAR) + '_'
  else
  PRINT 'Value_ _'
  SET @loopcounter = @loopcounter - 1
END

Это дает:

Value_10_
Value_ _
Value_8_
Value_ _
Value_6_
Value_ _
Value_4_
Value_ _
Value_2_
Value_ _
person mcelroyd    schedule 02.08.2019

person    schedule
comment
Будет более понятно и красиво, если вы сможете дать немного описания и сделать форматирование кода равномерным. - person RinoTom; 20.09.2013
comment
Это просто копия другого ответа с объяснением 0 - person jasttim; 24.04.2019