Масштабируемые типы с фиксированной точкой
Типы с фиксированной точкой позволяют управлять числами, которые должны вычисляться с дробной частью, задающейся количеством цифр после десятичной точки, или масштабом. Обычно масштабируемые типы нужны для финансовых значений и других чисел, которые являются результатом подсчета или выполнения арифметических операций над целыми элементами и частями элементов.
Предсказуемость результатов умножения и деления чисел с фиксированной точкой позволяет выбрать их для хранения денежных значений. Тем не менее, поскольку типы с фиксированной точкой имеют ограниченные "рамки", в которых могут размещаться числа, они являются причиной появления исключений переполнения или потери значимости возле их верхней и нижней границы. В странах, где единицы валюты представляют небольшие значения, нужно быть внимательным при выборе ограничений чисел.
Например, следующий оператор использует ставку налога (DECIMAL(5,4)) и чистую прибыль (NUMERIC (18, 2)):
UPDATE ATABLE
SET INCOME_AFTER_TAX = NET_PROFIT - (NET_PROFIT * TAX_RATE) ;
Пусть ставка налога будет 0.3333.
Пусть чистая прибыль будет 1234567890123456.78.
Результат:
ISC ERROR CODE:335544779
Integer overflow. The result of an integer operation caused the most significant bit of the result to carry.
(Переполнение целого числа. Результат целочисленной операции привел к переносу большинства значащих битов)
Firebird предоставляет два типа данных чисел с фиксированной точкой или масштабируемых: NUMERIC и DECIMAL. Каждый масштабируемый тип объявляется как TYPE(p, s), где p определяет точность (количество значащих цифр), а s - масштаб (размещение десятичной точки - т. е. количество цифр справа от символа десятичной точки).
В соответствии со стандартом SQL-92 оба типа NUMERIC и DECIMAL ограничивают хранимое число объявленным масштабом. Различие между этими двумя типами заключается в способе, каким ограничивается точность. Точность должна быть такой, "как объявлено" для столбцов типа NUMERIC, в то время как столбцы DECIMAL могут получать числа, чья точность по меньшей мере равна тому, что было объявлено, больше границы реализации.
Типы NUMERIC и DECIMAL, как они реализованы в Firebird, являются идентичными, за исключение случая, когда точность меньше пяти. Оба типа действительно соответствуют стандарту типа DECIMAL, NUMERIC не соответствует SQL-92.
Внутренне Firebird хранит масштабированное число как типы SMALLINT(16 бит), INTEGER (32 бита) или BIGINT(64 бита) в соответствии с объявленным размером точности. Его объявленная точность[21] сохраняется вместе с объявленным масштабом в виде отрицательного множителя масштаба[22], представляющего степень числа 10. Когда к числу происходит обращение для вывода или для расчетов, оно получается произведением хранимого целого на 10 в степени "множитель масштаба" ( 10(^множитель масштаба^) )
Например, для столбца, объявленного как NUMERIC(4,3), Firebird сохраняет внутренне число в виде SMALLINT. Если вы вводите число 7.2345, Firebird без сообщений округляет самую правую цифру (4) и сохраняет 16-битовое целое 7235 и множитель масштаба -3. Это число будет найдено как 7.235 (7235 * 10(^-3^)).
Тип данных NUMERIC
Формат типа данных NUMERIC:
NUMERIC(p,s)
Например, NUMERIC (4,2) определяет число, состоящее не более чем из четырех цифр, включая две цифры справа от десятичной точки. Следовательно, числа 89.12 и 4.321 будут сохранены в столбце NUMERIC(4,2) как 89.12 и 4.32 соответственно. Во втором примере последняя цифра 10(^-3^) выходит за пределы масштаба и просто отбрасывается.
И все же возможно хранение в этом столбце числа с большей точностью, чем было объявлено. Максимально здесь может быть 327.67 - т. е. число с точностью 5. Поскольку база данных хранит фактическое число как SMALLINT, числа не начинают вызывать ошибки переполнения, пока внутренне хранимое число не станет больше 32,767 или меньше -32,768.
Тип данных DECIMAL
Формат типа данных DECIMAL:
DECIMAL(Р, S)
Как и в случае NUMERIC, DECIMAL(4,2) определяет число, состоящее, по меньшей мере, из четырех цифр, включая две цифры справа от десятичной точки. Тем не менее, поскольку Firebird сохраняет данные DECIMAL с точностью 4 и меньше как INTEGER, этот тип может в столбце DECIMAL(4,1) потенциально хранить число от 214 748 364 до -214 748 364.8 без появления ошибки переполнения.
Точные числа могут быть перепутаны, не только по причине тонкой разницы между этими двумя типами, но и потому, что диалект базы данных влияет на доступный диапазон точности. Табл. 9.2 может служить руководством по тому, какую точность и масштаб вам нужно указать для различных требований к вашим числам.
Таблица 9.2. Диапазон и способ хранения в Firebird типов данных NUMERIC и DECIMAL
Точность |
Тип |
Диалект 1 |
Диалект 3 |
1-4 |
NUMERIC |
SMALLINT |
SMALLINT |
1-4 |
DECIMAL |
INTEGER |
INTEGER |
5-9 |
NUMERIC и DECIMAL |
INTEGER |
INTEGER |
10-18 |
NUMERIC и DECIMAL |
BIGINT |
DOUBLE PRECISION* |
* Точные числа с точностью больше чем 9, могут быть объявлены в диалекте 1 базы данных без появления исключения. Эти числа будут храниться как DOUBLE PRECISION и будут подчиняться тем же самым ограничениям точности, что и любые числа с плавающей точкой. В процессе преобразования базы данных диалекта 1 в диалект 3 клиент диалекта 2, открыв таблицу, содержащую столбцы DECIMAL или NUMERIC с точностью, большей 9, получит сообщение об ошибке. Более подробную информацию о преобразованиях диалекта 1 в диалект 3 см. в разд. "Специальная тема миграции: диалекты SQL" главы 8.
Конвертированные базы данных
Если база данных диалекта 1 была обновлена до диалекта 3 с использованием создания резервной копии gbak, а затем восстановлена, то числовые поля, определенные с точностью, большей 9, сохранят тип данных DOUBLE PRECISION. Хотя они будут представлены так, как были вначале определены (например, NUMERIC (15,2)), они будут сохраняться и использоваться в вычислениях как DOUBLE PRECISION.
Более подробную информацию о преобразованиях баз данных диалекта 1 в диалект 3 см. в разд. "Специальная тема миграции: диалекты SQL" главы 8.
Специальные ограничения в статическом SQL
Включающий язык встроенных приложений не может использовать или распознавать малые точности типов данных NUMERIC или DECIMAL С дробной частью, когда они внутренне хранятся как типы SMALLINT или INTEGER. для устранения такой проблемы в любой базе данных, к которой предполагается доступ из таких встроенных приложений (ESQL):
* не определяйте столбцы или домены с типом NUMERIC или DECIMAL малой точности в базе данных диалекта 1. или храните данные как целые и пишите в приложениях код, учитывающий масштаб, или используйте DOUBLE PRECISION и применяйте подходящий алгоритм округления для вычислений;
* в базе данных диалекта 3 определяйте столбцы или домены любого размера типа NUMERIC и DECIMAL, используя точность не меньше 10, чтобы получить их внутреннее хранение как BIGINT. Указывайте масштаб, если вы хотите управлять точностью и масштабом. Используйте ограничения CHECK, если вам нужно контролировать диапазоны значений.
Поведение типов с фиксированной точкой в операциях
При выполнении деления типов с фиксированной точкой диалекты 1 и 3 ведут себя по-разному.
В диалекте 3, когда оба операнда являются типами с фиксированной точкой, Firebird суммирует масштабы обоих операндов для определения масштаба результата (частного). Частное имеет точность 18. При проектировании запросов с выражениями, содержащими деление, убедитесь, что частное всегда будет иметь точность больше, чем любой из операндов, и примите меры предосторожности в случаях, когда точность потенциально может превысить допустимый максимум 18.