В таблице задан тип DECIMAL(5, 2). Предскажите, какая вставка завершится ошибкой из-за диапазона типа, и объясните механизм.
CREATE TABLE prices (
item_id INTEGER PRIMARY KEY,
amount DECIMAL(5, 2)
);
INSERT INTO prices VALUES (1, 999.99);
INSERT INTO prices VALUES (2, 1000.00);
Ошибкой завершится вторая вставка: значение 1000.00 не помещается в DECIMAL(5, 2). Первое число в скобках — общая точность типа, второе — количество цифр после десятичного разделителя; значит, до десятичного разделителя остаётся 5 - 2 = 3 цифры.
DECIMAL появился как точный числовой тип для хранения денежных сумм, процентов и других значений, где двоичная арифметика с плавающей точкой может давать ошибки округления. В отличие от приближённых типов, он задаёт фиксированное количество десятичных цифр и позволяет контролировать допустимый диапазон.
Для DECIMAL(5, 2) всего разрешено пять значащих цифр, из которых две находятся после десятичного разделителя. Поэтому максимально допустимое по модулю значение — 999.99; значение 1000.00 требует уже шести цифр: четырёх до разделителя и двух после него.
Если выбрать слишком маленькую точность, корректные бизнес-значения начнут отклоняться при вставке или обновлении. Если выбрать чрезмерно большую точность, возрастут требования к хранению и может стать менее очевидной проверка корректности данных.
В данном объявлении:
DECIMAL(5, 2) означает:
5 — максимум цифр во всём числе, без учёта знака и десятичного разделителя;2 — максимум цифр в дробной части;3 — максимум цифр в целой части.Поэтому 999.99 допустимо, а 1000.00 требует четырёх целых цифр и не соответствует объявленной точности. СУБД должна сообщить об ошибке переполнения числового типа, а не молча сохранить такое значение как 999.99.
Знак отрицательного числа не считается цифрой точности: -999.99 также укладывается в этот тип. При значении с большим количеством дробных цифр конкретная СУБД может применять правила округления или выдавать ошибку; такое поведение нужно проверять по документации используемой СУБД.
NUMERIC и DECIMAL относятся к стандартным точным числовым типам, хотя детали реализации и некоторые правила приведения типов могут различаться между СУБД. Для денежных значений обычно дополнительно задают NOT NULL и проверяют бизнес-ограничения отдельным CHECK.
Интернет-магазин хранит цену товара. Команда выбрала DECIMAL(5, 2), потому что большинство цен меньше тысячи, но затем появился товар стоимостью 1250.00. Вставка стала завершаться ошибкой, хотя число было корректным с точки зрения бизнеса.
Вариант с FLOAT решил бы проблему диапазона, но создал бы риск приближённых результатов при расчётах и сравнении денежных сумм. Вариант с DECIMAL(5, 2) сохранил бы текущую модель, но продолжил бы отклонять более дорогие товары.
Выбранное решение — увеличить точность, например до DECIMAL(10, 2), предварительно проверив существующие данные и требования к максимальной цене. Такой тип сохраняет точность до копеек и допускает значения до 99 999 999.99; результатом становится предсказуемое хранение денег без неоправданного перехода к приближённой арифметике.
1. Вопрос: Считается ли знак минус частью точности DECIMAL(p, s)?
Ответ: Нет. Точность относится к цифрам числа, а знак и десятичный разделитель в неё не входят. Поэтому DECIMAL(5, 2) допускает как 999.99, так и -999.99, если остальные ограничения столбца это разрешают.
2. Вопрос: Что произойдёт с числом 12.345, если столбец имеет тип DECIMAL(5, 2)?
Ответ: Универсально обещать один результат для всех СУБД нельзя. Некоторые реализации округляют значение до масштаба 2, получая 12.35, а другие режимы или операции могут завершиться ошибкой. В прикладном коде нельзя полагаться на неявное округление: нужное правило следует задать явно и проверить средствами конкретной СУБД.
3. Вопрос: Достаточно ли увеличить точность, чтобы разрешить большие суммы с тем же количеством знаков после разделителя?
Ответ: Да, если увеличить именно p, сохранив s. Например, переход от DECIMAL(5, 2) к DECIMAL(10, 2) увеличит число допустимых цифр в целой части с трёх до восьми. Но изменение типа является DDL-операцией: перед ним нужно учитывать совместимость существующих данных, индексов, ограничений и зависимого прикладного кода.