Поведение типов с фиксированной точкой в операциях
Поведение типов с фиксированной точкой в операциях
Деление
При выполнении деления типов с фиксированной точкой диалекты 1 и 3 ведут себя по-разному.
В диалекте 3, когда оба операнда являются типами с фиксированной точкой, Firebird суммирует масштабы обоих операндов для определения масштаба результата (частного). Частное имеет точность 18. При проектировании запросов с выражениями, содержащими деление, убедитесь, что частное всегда будет иметь точность больше, чем любой из операндов, и примите меры предосторожности в случаях, когда точность потенциально может превысить допустимый максимум 18.
В диалекте 1 деление всегда создает частное типа DOUBLE PRECISION.
Примеры
В диалекте 3 частное от деления DECIMAL(12,3) на DECIMAL(9,2) будет DECIMAL(18,5). Масштабы суммируются:
SELECT 11223344.556/1234567.89 FROM RDB$DATABASE
Это дает 9.09090.
Посмотрим, чем отличается частное, когда этот же запрос выполняется в диалекте 1. Первый операнд трактуется как число DOUBLE PRECISION, потому что его точность (12) превышает максимум масштабируемого типа для диалекта 1. Частное также является числом DOUBLE PRECISION. Результат 9.09090917308727 по причине ошибок, присущих типам с плавающей точкой.
Для следующей таблицы, определенной в диалекте 3, операции деления дают различные результаты.
CREATE TABLE t1 (
i1 INTEGER,
i2 INTEGER,
n1 NUMERIC(16, 2),
n2 NUMERIC(16,2));
COMMIT;
INSERT INTO t1 VALUES (1, 3, 1.00, 3.00);
COMMIT;
Следующий запрос возвращает значение 0.33 типа NUMERIC(18,2), потому что сумма масштабов 0 (операнд 1) и 2 (операнд 2) равна 2:
SELECT i1/n2 from t1
Следующий запрос возвращает значение 0.3333 типа NUMERIC (18,4), потому что сумма масштабов двух операндов равна 4:
SELECT n1/n2 FROM t1
Деление целого на целое
Используя предыдущий пример, следующий запрос в диалекте 3 вернет целое 0, потому что каждый операнд имеет масштаб 0, следовательно, сумма масштабов будет 0:
SELECT i1/i2 FROM t1
В диалекте 1, как и в большинстве других СУБД, деление одного целого на другое целое даст результат с плавающей точкой типа DOUBLE PRECISION:
SELECT 1/3 AS RESULT FROM RDB$DATABASE
Это дает .333333333333333.
Хотя настоящее правило диалекта 1 является интуитивным для языков программирования, оно не соответствует стандарту SQL-92. Целые типы имеют масштаб 0. Для согласованности это требует, чтобы результат (частное) любой операции деления целого на целое соответствовал правилам масштабирования для чисел с фиксированной точкой и был бы целым.
Диалект 3 соответствует стандарту и усекает частное от операций деления целого на целое до целого. Следовательно, следующий оператор является неразумным:
SELECT 1/3 AS RESULT FROM RDB$DATABASE
Он вернет 0.
Если вам нужно сохранить дробную часть в результате (в частном) деления целого на целое в диалекте 3, убедитесь, что у одного из операндов присутствует нужный масштаб, или включите "множитель" в выражение, чтобы гарантировать масштаб результата.
Примеры:
SELECT 1.00/3 AS RESULT FROM RDB$DATABASE
Вернет .33.
SELECT (5 * 1.00)/2 AS RESULT FROM RDB$DATABASE
Этот вернет 2.50.
Диалект 1 базы данных с диалектом 3 клиента
База данных диалекта 1, которая была открыта клиентом диалекта 3, может преподнести некоторые сюрпризы в отношении деления целых чисел. Когда операция выполняет нечто, что приводит к проверке условия CHECK, или выполняется хранимая процедура или триггер, то осуществляемые действия основаны на диалекте, на котором CHECK, хранимая процедура или триггер были определены, а не на действующем диалекте, на котором приложение выполняет проверку, хранимую процедуру или триггер.
Например, в базе данных диалекта 1 таблица содержит столбцы MYCOL1 (INTEGER) и MYCOL2 (INTEGER) и следующее условие CHECK, которое было определено, когда база данных имела диалект 1:
CHECK(MYCOL1 / MYCOL2 >0.5)
Пусть теперь пользователь запускает isql или приложение, задав диалект 3. Программа пытается добавить строку в конвертированную базу данных:
INSERT INTO MYTABLE (COL1, COL2) VALUES (2,3);
Поскольку ограничение CHECK было определено в диалекте 1, оно вернет частное 0.666666666666667, и строка будет соответствовать условию CHECK.
Обратное также верно. Если то же ограничение CHECK было добавлено в базу данных диалекта 1 из клиента диалекта 3, то для ограничения будет сохранена арифметика диалекта 3. Предыдущий оператор INSERT не будет выполнен, потому что проверка вернет значение частного 0, которое нарушает это ограничение.
! ! !
СОВЕТ. Мораль всего этого: используйте базы данных диалекта 3 и всегда соединяйтесь с ними, применяя диалект 3. Если вы собираетесь использовать Firebird, то обновите все существующие базы данных до диалекта 3 - желательно описав новую базу данных и поместив в нее ваши старые данные - таким образом вы сохраните покой и сможете избежать уймы неприятных сюрпризов.
. ! .
Умножение и деление
Если оба операнда являются точными числами, умножение операндов даст точное число с масштабом, равным сумме масштабов операндов. Например,
CREATE TABLE t1 (
n1 NUMERIC(9,2),
n2 NUMERIC (9,3) ) ;
COMMIT;
INSERT INTO t1 VALUES (12.12, 123.123);
COMMIT;
Следующий запрос возвращает число 1492.25076, потому что n1 имеет масштаб 2, а n2 - масштаб 3. Сумма масштабов 5.
SELECT n1*n2 FROM t1
В диалекте 3 точность результата умножения чисел с фиксированной точкой будет равна 18. Нужно принять меры предосторожности, чтобы быть уверенным, что не будет переполнения результата при распространении масштаба в умножении.
В диалекте 1, если распространение масштаба приводит к тому, что вычисление даст результат с точностью больше 9, то результатом будет DOUBLE PRECISION.
Сложение и вычитание
Если все операнды являются точными числами, то сложение и вычитание операндов даст точное число с масштабом, равным максимальному масштабу операндов. Например,
CREATE TABLE t1 (
n1 NUMERIC(9, 2) ,
n2 NUMERIC(9, 3)) ;
COMMIT;
INSERT INTO t1 VALUES (12.12, 123.123);
COMMIT;
SELECT n1 + n2 FROM t1;
Этот запрос возвращает 135.243, выбирая максимальный масштаб операндов. Аналогично, следующий запрос возвращает число -111.003:
SELECT n1 - n2 FROM t1;
В диалекте 3 результат любого сложения или вычитания имеет тип NUMERIC(18,n). В диалекте 1 он имеет тип NUMERIC (9, n), где n - масштаб максимального операнда.