Преобразование арифметического переполнения в 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, проблема решилась сама собой.
Таким образом, исходный вопрос был неверной - проблема лежала в несколько слоев.
Спасибо тем, кто внес свой вклад.