Página principal
Artículos y trucos
Catálogo de productos
Ejemplos y descargas
Mis libros
Cursos de formación
Investigación y desarrollo
Libros recomendados
Mis páginas favoritas
Acerca del autor
 
En colaboración con Amazon
 
Intuitive Sight

Los tipos numéricos de InterBase

Mi objetivo es mostrarle al lector qué es lo que sucede realmente en InterBase cuando utilizamos los tipos numeric y decimal de SQL estándar. Podemos utilizar estos tipos en la declaración de columnas de tablas, en la creación de dominios, en variables locales y en parámetros de procedimientos almacenados. La principal peculiaridad de ambos consiste en que podemos adjudicarles una precisión y una escala. La precisión es el número de dígitos, tanto a la derecha como a la izquierda de la coma decimal, que se pueden representar mediante el tipo. De esa cantidad, la escala roba unos cuantos dígitos para que se coloquen a la derecha de la coma. Por supuesto, la escala debe ser siempre menor o igual que la precisión:

create tabla Empleados (
   Codigo     numeric(9) not null,     /* Lo mismo que numeric(9,0) */
   Peso       numeric(6,3),            /* Peso en kilogramos y gramos */
   Patrimonio numeric(15,2),
   /* ... */
);

En la actual implementación de InterBase, numeric y decimal son totalmente equivalentes. La precisión máxima permitida actualmente es de 15 dígitos, pero probablemente cambie en futuras versiones (cambió en InterBase 6, a 18 dígitos; este truco data de la época de InterBase 5.x).

Cuando un programador proveniente de xBase ve estos tipos los asocia inmediatamente con el tipo numérico de dBase. En este sistema los valores numéricos se representan en formato ASCII (todo un despilfarro), y está claro que es importante especificar un tamaño máximo para ahorrar espacio. Por lo tanto, este programador hará un uso intensivo de numeric. ¿Hace bien o mal? Le explico lo que hace InterBase, y ya usted decidirá.

Sorpresa número uno: Desde el punto de vista del espacio ocupado para su almacenamiento, las dos columnas siguientes son iguales:

   /* ... */
   Altura   numeric(3,2),
   Peso     numeric(4,2),
   /* ... */

Lo que sucede es que InterBase elige siempre uno de los tres tipos siguientes para representar físicamente el valor: smallint, integer o double precision. La elección está determinada por la precisión especificada, y se ignora la escala:


 Precisión  Tipo base Espacio necesario 
1..4SMALLINT2 bytes
5..9INTEGER4 bytes
10..15DOUBLE PRECISION8 bytes

¿Y qué pasa con la escala? Si definimos una columna como numeric(3,2), por ejemplo, sus valores se almacenan multiplicados por 100, para obtener un número entero. La escala se guarda en una tabla del sistema, y se utiliza para restaurar el número original durante la evaluación de expresiones por InterBase. Si, por el contrario, la precisión es superior a 9, el número se almacena directamente con el formato de double precision, como ya he dicho. Este tipo se representa con 8 bytes, y puede contener sin dificultad alguna los 15 dígitos de precisión mencionados.

Sorpresa número 2: El BDE tiene problemas para trabajar con los tipos de campos anteriores, especialmente cuando la precision es menor que 10. Cuando traemos un campo numérico a Delphi y la configuración del BDE es la implícita, se crean objetos de tipo TIntegerField o TFloatField, de acuerdo a la precisión. Por supuesto, en un campo entero se truncarán los decimales o, en versiones anteriores del BDE, el número aparecerá multiplicado por el factor de escala. Y los campos TFloatField ignorarán el número de dígitos decimales, mostrando cuantos decimales estimen pertinentes.

Una solución consiste en activar el parámetro ENABLE BCD en la configuración del alias del BDE. En tal caso, siempre se traen campos TBCDField, que utilizan el tipo Currency internamente para almacenar los valores. Sin embargo, estos campos tienen una limitación: solamente pueden representar hasta 4 dígitos decimales. Así que no abuse de ellos.

Finalmente, ¿está preparado para la Sorpresa número 3? Resulta que para InterBase los siguientes tipos no solamente se representan del mismo modo, ¡sino que son indistinguibles entre sí!

   /* ... */
   Columna1   numeric(5,2),
   Columna2   numeric(8,2),
   /* ... */

Esto sucede porque InterBase no almacena en las tablas de sistema la precisión, sino solamente el factor de escala más el tipo base elegido para representarlos. Los dos tipos anteriores son idénticos a numeric(9,2). El tipo numeric(5) es indistinguible respecto a un integer. Si quiere comprobarlo, llame al SQL Explorer, abra un alias de InterBase y ejecute la siguiente instrucción SQL:

create domain DomPrueba as numeric(5,2)

Ahora sitúese en la rama del árbol de la izquierda correspondiente a los dominios, y pulse Ctrl+R para actualizar el árbol. Seleccione el nodo DomPrueba y en el panel de la derecha active la pestaña Text. Posiblemente le sorprenda ver que InterBase "cree" que la definición del dominio es:

CREATE DOMAIN DomPrueba AS
  NUMERIC(9,2)

Si lo desea, puede ejecutar también la siguiente consulta:

select *
from   RDB$FIELDS
where  RDB$FIELD_NAME = 'DOMPRUEBA'

Observe entonces que la columna rdb$field_length contiene sencillamente el valor 4, que corresponde a un entero, y que no hay más información sobre la precisión utilizada en la definición.

¿Conclusiones? Estas son las mías:

  • No merece la pena especificar una precisión y escala para columnas numéricas, pues InterBase siempre utilizará uno de sus tipos binarios nativos para representarlas.
  • Tampoco merece el esfuerzo utilizar precisión y escala si lo que necesitamos es restringir el rango de valores admitidos por la columna. Como hemos visto, dentro de cierto intervalo de valores de precisión, InterBase es incapaz de distinguir entre ellos. Si quiere limitar un rango de valores utilice cláusulas check.