Преобразование арифметического переполнения в int при вставке 2147483648 или больше в столбец decimal(18,6) в таблице базы данных SQL Server

У меня возникла эта проблема в SQL Server 2012, но она также проявляется в SQL Server 2017.

Моя таблица базы данных, назовите это MyTable, содержит decimal(18,6) столбец new_balance, который не допускает значения NULL. Таблица также содержит два целочисленных столбца, которые являются частью первичного ключа. Других целочисленных полей нет.

Хранимая процедура имеет параметр @new_balance который также определяется как decimal(18,6). Также есть параметры для id (@id) и seq_num (@seq_num), оба определены как int.

Процедура выполняет вставку в таблицу по строкам:

INSERT INTO MyTable (id, seq_num, new_balance)
VALUES (@id, @seq_num, @new_balance);

когда @new_balanceустановлено значение 2147483647.999999 или ниже, вставка выполняется должным образом.
когда@new_balanceустановлен на число, большее или равное 2147483648, например 2147483648.1, процедура завершается ошибкой из-за арифметического переполнения, преобразующего выражение в int error. Я понимаю, что максимальное значение для столбца int составляет 2147483647.

То же самое и со следующей вставкой:

INSERT INTO MyTable (id, seq_num, new_balance)
SELECT @id, @seq_num, @new_balance;

Учитывая, что и параметр, и столбец таблицы определены как decimal(18,6), Я изо всех сил пытаюсь понять, почему происходит преобразование в int как часть вставки (особенно со вторым оператором, где я не ожидал бы никакого неявного преобразования).

Я также пробовал использовать @new_balance к decimal(18,6) явно как часть INSERT заявление:

INSERT INTO MyTable (id, seq_num, new_balance)
SELECT @id, @seq_num, CAST(@new_balance AS decimal(18,6));

Это тоже не сработало.

Как ни странно, он отлично работает, если я указываю в запросе табличную переменную с теми же определениями и выполняю аналогичную вставку:

DECLARE @MyTable TABLE (id int, seq_num int, new_balance decimal(18,6));
INSERT INTO @MyTable (id, seq_num, new_balance)
SELECT @id, @seq_num, @new_balance;

Я пробовал этот подход в процедуре, т.е. сначала вставлял запись в @MyTable а затем пытаюсь вставить в MyTable следующим образом:

DECLARE @MyTable TABLE (id int, seq_num int, new_balance decimal(18,6));
INSERT INTO @MyTable (id, seq_num, new_balance) SELECT @id, @seq_num, @new_balance;
INSERT INTO MyTable (id, seq_num, new_balance) SELECT id, seq_num, new_balance FROM @MyTable;

Это тоже не сработало.

Для полноты я также попытался создать запись с нулевым значением, а затем обновить существующую запись - опять же, это было неудачно, поэтому эта проблема возникает как с INSERT, так и с UPDATE:

INSERT INTO MyTable (id, seq_num, new_balance) SELECT @id, @seq_num, 0.00;

Это нормально, но следующий шаг дает ту же ошибку, что и описано выше:

UPDATE MyTable SET new_balance = @new_balance WHERE id = @id AND seq_num = @seq_num;

Чтобы подтвердить, нет INSTEAD OF INSERT триггер или AFTER UPDATE триггер на столе - никаких триггеров нет.

Отслеживание изменений не включено в базе данных, и для этого конкретного поля нет ограничений - есть ограничения по умолчанию для других десятичных (18,6) полей, которые присутствуют в реальной таблице.

В базу данных также не добавлялись представления.

Мой первый вопрос на этом форуме - надеюсь, кто-то сам испытает это и, возможно, знает, как решить, хотя я не смог найти вопрос подобного характера. Я в тупике.

1 ответ

Решение

Имея возможность вставить данные непосредственно в таблицу вне процедуры, я просмотрел некоторые последующие события и выявил проблему:

После вставки записи система обновляет статус связанной записи в родительской таблице. В этой родительской таблице есть ПОСЛЕ ОБНОВЛЕНИЯ, которое определяет это изменение статуса как требующее аудита и запускает вторичную процедуру для создания записей истории аудита. В рамках этого захвата система преобразует старые и новые значения в MyTable в удобный для пользователя формат с помощью скалярной функции. Как часть этого, значение разделено на две части для создания необходимого форматирования, и целая числовая часть была приведена как int. Если изменить это приведение на bigint, проблема решилась сама собой.

Таким образом, исходный вопрос был неверной - проблема лежала в несколько слоев.

Спасибо тем, кто внес свой вклад.

Другие вопросы по тегам