T-SQL: как отключить проверку переполнения для целочисленных операций?

В C# у нас есть unchecked для отключения проверки переполнения для целочисленных операций.

int int1;
unchecked
{
    int1 = 2147483647 + 10;
}

Целочисленный арифметический результат будет равен -2 147 483 639.

Но в T-SQL я не могу найти способ отключить проверки границ

DECLARE @INT1 INT
SET @INT1 = 2147483647 + 10

Результаты с ошибкой:

Сообщение 8115, уровень 16, состояние 2, строка 2
Ошибка арифметического переполнения при преобразовании выражения в тип данных int.


person BaltoStar    schedule 01.03.2018    source источник
comment
Я не думаю, что в TSQL есть такая вещь.   -  person Zohar Peled    schedule 02.03.2018
comment
Я обновил свой ответ, чтобы предоставить более аккуратный пример того, как получить результат BigInt из вычислений и преобразовать его в Int без переполнения способом, подходящим для использования в запросе или функции.   -  person HABO    schedule 04.03.2018


Ответы (3)


Не то чтобы я думаю, что это обязательно отличная идея, но вы можете обернуть ее в try/catch.

declare @int int;

begin try
  set @int = 2147483647 + 10
end try
begin catch
  if ERROR_NUMBER() = 8115 
    set @int = 2147483647
  else 
    select
        ERROR_NUMBER() AS ErrorNumber  
       ,ERROR_MESSAGE() AS ErrorMessage;  
end catch 

select @int

Результат:

2147483647
person Eric Brandt    schedule 01.03.2018

Выполнить эту операцию (2147483647 + 10) невозможно, так как тип данных INT находится в диапазоне от -2 147 483 648 до 2 147 483 647, согласно спецификация Microsoft.

Вместо этого вы можете использовать тип данных BIGINT, например:

DECLARE @number BIGINT;
SET @number = CAST(2147483647 AS BIGINT) + 10;
SELECT @number;
-- Output: 2147483657, as expected
person Pedro Martins    schedule 02.03.2018

Вы можете преобразовать значения int в bigint, выполнить сложение, а затем обработать переполнение при преобразовании обратно в int с использованием выражения case таким образом:

declare @MaxInt as BigInt = Power( Cast( 2 as BigInt ), 31 ) - 1;
declare @Foo as Int = 2147483647;
declare @Bar as Int = 10;
declare @BigSum as BigInt;
set @BigSum = @Foo + Cast( 10 as BigInt );
select @MaxInt as MaxInt, @Foo as Foo, @Bar as Bar, @BigSum as BigSum,
  case when @BigSum <= @MaxInt then Cast( @BigSum as Int ) else
    Cast( @BigSum - Power( Cast( 2 as BigInt ), 32 ) as Int ) end as Unsigned;

Обновление: более аккуратный пример, показывающий, как взять результат BigInt (из предыдущих вычислений) и преобразовать его в Int без переполнения:

declare @Samples as Table ( Value BigInt );
insert into @Samples ( Value ) values
  -- NB: At least one literal value needs to be cast as a BigInt to get BigInt values.  No, really.
  ( Cast( 0x0 as BigInt ) ), ( 0x1 ), ( 0x7FFFFFFF ), ( 0x80000000 ), ( 0x80000001 ), ( 0xFFFFFFFF ),
  ( -1 ), ( 0x4200000001 ), ( 0x7080000000 ); -- Some BigInt values beyond 32 bits.

select Value, Cast( Value as Binary(8) ) as ValueHex,
  Value & 0xFFFFFFFF as LSB32, Cast( Value & 0xFFFFFFFF as Binary(8) ) as LSB32Hex,
  Cast( case when Value & 0x80000000 = 0 then Value & 0xFFFFFFFF else
    ( Value & 0x7FFFFFFF ) - Cast( 0x80000000 as BigInt ) end as Int ) as IntResult
  from @Samples;

Преобразование может быть объединено в UDF, хотя производительность может пострадать.

person HABO    schedule 02.03.2018