Archive for junio, 2010

Modelamiento de base de datos

sábado, junio 19th, 2010

Introducción

Siempre existen diferencias al momento de modelar bases de datos, ya sea por la forma en cómo se fue educado en esta área, como las costumbres que se fueron adoptando en la vida laboral. Es por eso que muchas veces se necesitan acuerdos y/o restricciones del como modelar ciertas características de las bases de datos, esto para que se logre una estandarización del trabajo.
La estandarización es compleja, lo más complicado es prever un estándar que apoye la velocidad de desarrollo, que de la posibilidad de encuentro rápido de errores y que pueda ser ‘leído’ de una forma similar por todos los que participan del desarrollo y mantención.
Se presenta un trabajo sobre el modelamiento de bases de datos y la aplicación de estándares dentro del modelamiento.

Nombramiento

Nombramiento de tablas

El nombre de la tabla debe ser representativo para la entidad en cuestión, tratando de evitar la ambigüedad. El nombre debe ser en singular si la clave primaria se conforma de un campo, se utilizará plural cuando exista una clave compuesta, ya que esto indica que existe un concepto que se repite más de una vez dentro de la tabla, ese concepto es el que debiese estar en plural.

En el ejemplo se representa lo descrito anteriormente, se puede discutir sobre el nombre de la tabla que relaciona a ambas, esto porque el nombre es CATEGORIAS_CLIENTES, esto implica a primera vista que un cliente puede estar en muchas categorías, y una categoría puede contener muchos clientes. Si el nombre fuese cambiado por ejemplo a CATEGORIA_CLIENTES, significaría que una categoría puede tener muchos clientes y un cliente solo podría estar en una categoría. Si se dan cuenta existe la sutileza que la tabla CATEGORIA_CLIENTES tendría la misma estructura de CATEGORIAS_CLIENTES, si la dibujásemos serían idénticas. Debido a que la estructura de la tabla lo permite, igualmente podríamos introducir varios clientes a varias categorías y cada categoría contener más de un cliente, por lo mismo, para lograr el uso correcto con el significado que se le está dando al nombramiento se debiese generar un trigger que hiciese respetar el significado que le deseamos dar al modelamiento.

Prefijo del nombre de la tabla

En general las tablas no debiesen tener ningún prefijo específico, sobre todo cuando existe un buen manejo de esquemas o bases de datos en general. Por ejemplo, cuando se crea un esquema por cada negocio o escenario que se atiende tendríamos separadas las tablas en su esquema o base de datos respectiva, por ejemplo: CONTABILIDAD, INVERSIONES, TESORERIA, etc.
La necesidad de prefijo surge cuando, por ejemplo, al desarrollar el sistema de inversiones, que ocupa el esquema o base de datos INVERSIONES te establecen la restricción de no escribir directamente al esquema CONTABILIDAD, por lo que debes generar tablas de paso en tu esquema de base de datos, para luego, por algún otro proceso enviar los datos hacia el esquema de CONTABILIDAD. Otro punto en donde se genera la necesidad de un prefijo es cuando por algún motivo tenemos solo una base de datos o esquema disponible, la mayoría de las veces por algún tipo de restricción del cliente, así la posibilidad de que el escenario que estamos evaluando tenga muchos subescenarios distintos dentro del mismo desarrollo surge la necesidad de dar un prefijo asociado en la tabla, para tener un mayor orden tanto en la lectura del modelo, así como en la utilización de este, ya que con lectores o administradores de bases de datos disponibles las tablas con el mismo prefijo te saldrán agrupadas por culpa de su ordenamiento alfabético. Por ejemplo:

Prefijo para las tablas del escenario contabilidad CNT_
Prefijo para las tablas del escenario inversiones INV_
Prefijo para las tablas del escenario facturación FCT_

En general es bueno utilizar de tres a cuatro letras para la identificación del escenario, para decidir que letras también hay que proveer una estructuración para que dos personas siempre lleguen a prefijos similares; por ejemplo la siguiente: mantener la primera letra con que se nombra al escenario, sea vocal o consonante, luego de eso sólo ocupo las consonantes, a menos que se diera el caso que el escenario este compuesto de dos palabras en donde trato de ocupar la primera letra de la primera palabra y luego las consonantes de la segunda palabra siguiendo las consideraciones ya explicadas. Ejemplos de utilización de la norma de nombramiento:

CONTABILIDAD prefijo CNT
CONTABILIDAD_TESORERIA prefijo CTS
INVERSIONES prefijo INV
INVERSIONES_RESUMIDAS prefijo IRS

Siguiendo esta fórmula es fácil hacer que un grupo de diseñadores llegue siempre a nombres similares.

Tablas temporales

En más de una oportunidad existe la necesidad de generar tablas temporales, para datos que son de paso, nótese que es temporal para el caso de los datos, no para el caso de la estructura, o sea, no es que se elimine la tabla para luego construir la misma pero con otra estructura (créanme que ese tipo de solicitudes existen de parte de clientes y de hecho muchos utilizan este tipo de tablas). Aun cuando las ocupo, las evito completamente, pero cuando el cliente o el modelo realmente lo exige sugiero adicionar luego del prefijo TMP si pertenece al esquema o _TMP_ si pertenece al subescenario. Esto nos ayuda a tener agrupadas todas las tablas temporales. Debe quedar claro que en las tablas temporales se borrarán los datos, o sea, son solo para guardado de resultados parciales que por motivos de velocidad o estructuración, pero no se debe esperar que en la tabla permanezcan los datos.
Asociados a tablas temporales también debe existir un Jobs o proceso batch posterior que limpie la información con n tiempo de antigüedad. Puede ser que el mismo proceso que genera la información borre las tablas, pero el borrado igual requiere tiempo, por lo que para hacer más veloz el proceso que utiliza los datos temporales en general el borrado debe hacerse después.

Nombramiento de claves primarias

El nombre de una clave primaria debe ser PKY_ seguido del nombre de la tabla, de esta forma se podrá identificar la tabla o el problema de inserción cuando suceda, ya que el error saldrá relacionado con el nombre del constraint. Es habitual encontrar en modelamientos que las claves primarias comiencen con el prefijo PK_, el problema es que en algunos motores de base de datos podemos encontrar paquetes (packages) que mas adelante se definirán con prefijo PKG_, entonces utilizo PKY_ para un orden más especifico.
Si creásemos la tabla CLIENTE en un motor de bases de datos Oracle en un esquema ARO pero sin dar nombre a la clave primaria, tendríamos:

CREATE TABLE CLIENTE  (
RUT       NUMBER NOT NULL PRIMARY KEY,
DV        VARCHAR2(1),
NOMBRE    VARCHAR2(50),
A_PATERNO VARCHAR2(50),
A_MATERNO VARCHAR2(50)
);

Esto crea la tabla con RUT como clave primaria pero no existe un nombre asociado a la clave primaria de la tabla por lo tanto al intentar insertar dos veces el mismo registro se tiene:

INSERT INTO CLIENTE (RUT, DV, NOMBRE, A_PATERNO, A_MATERNO) VALUES (1,9,'Nombre', 'Paterno','Materno');

Error que empieza en la línea 9 del comando:
INSERT INTO CLIENTE (RUT, DV, NOMBRE, A_PATERNO, A_MATERNO) VALUES (1,9,'Nombre', 'Paterno','Materno')
Informe de error:
Error SQL: ORA-00001: unique constraint (ARO.SYS_C0079521) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key.

Aparece un nombre que fue asignado por el motor de base de datos ARO.SYS_C0079521 lo que no nos dice nada. En contraposición, la forma correcta sería:

CREATE TABLE CLIENTE  (
RUT       NUMBER NOT NULL,
DV        VARCHAR2(1),
NOMBRE    VARCHAR2(50),
A_PATERNO VARCHAR2(50),
A_MATERNO VARCHAR2(50),
CONSTRAINT PKY_CLIENTE PRIMARY KEY (RUT)
);

Con esto el error anterior sería más localizado:

Error que empieza en la línea 11 del comando:
INSERT INTO CLIENTE (RUT, DV, NOMBRE, A_PATERNO, A_MATERNO) VALUES (1,9,'Nombre', 'Paterno','Materno')
Informe de error:
Error SQL: ORA-00001: unique constraint (ARO.PKY_CLIENTE) violated
00001. 00000 -  "unique constraint (%s.%s) violated"
*Cause:    An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action:   Either remove the unique restriction or do not insert the key.

Ahora se tiene que el error es en una PKY_CLIENTE, que nos lleva inmediatamente a la tabla CLIENTE, por lo que el log se hace muchísimo más entendible.

Referencias

El nombre de una referencia esta dada por las tablas que participan. Utilizo el prefijo REF_ más el nombre de la tabla origen de la referencia y el nombre de la tabla destino. En el ejemplo se tiene que existen dos referencias:

REF_CATEGORIAS_CLIENTES_CLIENTE, que es la referencia desde la tabla CATEGORIAS_CLIENTES hacia la tabla CLIENTE, lo que significa que en la tabla origen CATEGORIAS_CLIENTES existe la clave primaria de la tabla CLIENTE.
REF_CATEGORIAS_CLIENTES_CATEGORIA que es la referencia desde la tabla CATEGORIAS_CLIENTES hacia la tabla CATEGORIA, lo que significa que en la tabla origen CATEGORIAS_CLIENTES existe la clave primaria de la tabla CATEGORIA.

No es necesario que en la tabla origen las referencias sean parte de las claves primarias, obviamente es un caso puntual. Por ejemplo, aquí no se tiene a sexo como parte de la clave primaria.

Una acotación, en el motor Oracle los nombres de los objetos no deben superar los 30 caracteres, por lo mismo en la imagen del modelo los nombres de las referencias aparecen cortados a ese largo.
Existen ocasiones en donde las tablas de origen y destino son las mismas para dos o mas referencias pero tienen un significado distinto, por lo que al finalizar establezco el diferenciador a las referencias. Por ejemplo, asumamos que deseamos modelar que nuestro cliente puede invitar a otros clientes, y deseamos guardan quien recomendó a quien, son dos relaciones desde la misma tabla origen a la misma tabla destino, por lo que bajo la normativa de nombramiento debiese tener el mismo nombre, es por eso que debemos agregarle un diferenciador. Ejemplo:

Por lo tanto si nos da un error en REF_INVITADO_CLIENTE_INVITADO sabemos que el error es que nos falto el RUT_INVITADO, si es en REF_INVITADO_CLIENTE_INVITADOR, sabemos que el error esta en el RUT_CLIENTE.

Nombramiento de índices

Se utiliza el prefijo IDX_ adicionalmente se establece un sufijo con los caracteres C o N para indicar clusterizado o no y U o N para indicar si es único o no único. Finalmente pongo algún nombre representativo para el índice. Aquí se puede además utilizar un acrónimo de tres letras para la tabla, para utilizarlo después del prefijo, esto también es útil para la ordenación de los índices. Ejemplo:
IDX_CLI_ID_SEXO_NN indicaría un índice en la tabla cliente (asumiendo que CLI es el acrónimo de esta tabla) es sobre el campo ID_SEXO, no clusterizado y no único.
Para algunos puede ser paranoico el tema de nombramiento de índices, pero se transforma en una buena herramienta cuando se utiliza explain plain o herramientas similares, estas herramientas nos permiten saber si una consulta ‘se va’ por un índice o no, y nos otorgan la posibilidad de dudar del diseño que existe detrás del índice, ya que existen algunos índices que no son adecuados para las tablas, porque en muchos casos empeora el rendimiento de las consultas asociadas.

Nombramientos de chequeos (o reglas)

Cuando existe la posibilidad de nombrar los chequeos utilizo el prefijo CHK_ y luego el nombre representativo para el chequeo. El acrónimo creado para la tabla también es útil acá.

Nombramiento de campos

En general el nombre del campo es representativo al atributo que se desea modelar, por lo tanto RUT será un Rol Único Tributario, NOMBRE será el nombre, y así, no hay necesidad de complejizarlo a menos que sea absolutamente necesario.

¿Cuándo es necesario?

Existen usuarios que conocen la base de datos, esos usuarios tienen cargos de importancia, y en algunos casos modifican directamente los datos que un proceso hizo, luego del tiempo aparecen los escándalos por fraude, o en otros casos, aparecen datos anómalos. Por lo tanto aparecen necesidades que no son de los modeladores de base de datos, sino que son de negocio, y que busca evitar que los usuarios modifiquen directamente datos que son realmente campos de cuidado, por ejemplo consolidaciones de balance, valorizaciones de cartera, etc.
Entonces, para las empresas que quieran modelos de datos sean más difíciles de entender a esos usuarios, o que por ventas de bases de datos quieren complicar alguna ingeniería reversa es bueno llegar a un consenso de nombramiento de campos de forma que tenga grados de dificultad para su traducción o verbalización pero que sean entendibles para el desarrollador. Realmente este punto es más útil para las empresas paranoicas con su seguridad sobre sus bases de datos.
Lo siguiente es una propuesta para ello.

Prefijo

Comencemos por un prefijo que corresponde a tres caracteres que sirven de acrónimo para la tabla.

INSTRUMENTOS todos los campos comenzaran con INS_
PERSONA todos los campos comenzaran con PRS_

También es útil para disminuir un error muy común, el de las columnas ambiguas, que ocurre cuando haces un select a dos tablas que contienen un campo del mismo nombre, si bien es cierto un desarrollador ordenado evitaría que le ocurriera, muchas veces pasa, incluso en proyectos en funcionamiento. Puede pasar que la segunda tabla hoy no tenga el campo, pero mañana con algún nuevo desarrollo ese campo se incluya dentro de esta y algunas consultas ya hechas acusen el error.

Sufijo

En algunos casos dependiendo como sea la forma de trabajo del equipo de desarrollo es útil considerar un sufijo asociado al tipo de dato o al rol que juega el campo dentro de la tabla, pera este cado se tiene

_PK para todas las que participan en la clave primaria
_FK para representar todas las que provienen de otra tabla
_NI para especificar números enteros
_ND para especificar números decimales
_CR para especificar caracteres
_RE para especificar campos si/no o campos de un solo carácter que indiquen una regla
_FC para campos fecha
_TM para campos timestamp, que incluyen fecha y hora

Mayor seguridad en el nombre

Como se vió si se utiliza prefijo y sufijo se tiene un campo más o menos así
XXX_nombreDescriptivo_XX
En el caso de asegurar el desarrollo de un producto y hacer más difícil la comprensión del campo el nombre descriptivo se escribirá como un par de conjunto de tres letras, de forma de nombrar el campo, quedando de la forma
XXX_XXX_XXX_XX