Bases de datos
Del modelo lógico a las tablas
Cómo cada elemento del diagrama se convierte en tabla, columna, clave primaria y clave foránea, y qué SQL sale de eso.
Una traducción mecánica
Todo el trabajo difícil ya está hecho. Pasar del modelo lógico al modelo relacional —tablas y columnas— es aplicar una tabla de equivalencias, y es tan mecánico que Data Modeler lo hace con un botón.
Lo que sigue es esa traducción, para que entiendas qué produce la herramienta en vez de aceptarlo a ciegas.
| Modelo lógico | Modelo relacional |
|---|---|
| Entidad | Tabla |
| Instancia | Fila |
| Atributo | Columna |
Atributo # (UID) | PRIMARY KEY |
Atributo * | Columna NOT NULL |
Atributo o | Columna que acepta NULL |
| Relación 1:N | FOREIGN KEY en el lado “muchos” |
| Relación N:M (ya resuelta) | Tabla intermedia con dos FOREIGN KEY |
| UID barrado | PRIMARY KEY compuesta por las claves foráneas |
También cambia el vocabulario, y conviene tener las dos columnas en la cabeza porque la bibliografía mezcla los dos:
| Lógico | Relacional |
|---|---|
| Entidad | Tabla (table) |
| Atributo | Columna (column) |
| UID | Clave primaria (primary key, PK) |
| Relación | Clave foránea (foreign key, FK) |
Dónde va la clave foránea
Es la única decisión que confunde, y tiene una regla de una línea:
La clave foránea va siempre del lado de la pata de gallo, o sea, en el lado “muchos”.
PROFESOR CURSO
┌──────────────┐ ┌──────────────┐
│ # id_profesor│- - - - - - - - - - - <│ # codigo │
│ * nombre │ │ * nombre │
└──────────────┘ │ * horas │
│ * id_profesor│ ← la FK aterriza acá
└──────────────┘El motivo es simple si lo piensas en filas: cada curso tiene un profesor, así que en la fila del curso cabe perfectamente una columna con el id de ese profesor. Al revés no funcionaría: un profesor dicta varios cursos, y en su fila no cabría una lista de códigos — sería otra vez un atributo multivaluado.
Y la opcionalidad del extremo también viaja:
| El extremo hacia el “uno” era | La columna FK queda |
|---|---|
| Continuo (obligatorio) | NOT NULL |
| Punteado (opcional) | Acepta NULL |
Es decir: si un curso debe tener profesor, id_profesor es NOT NULL; si pudiera existir sin profesor asignado, aceptaría NULL.
El instituto convertido en tablas
Partiendo del modelo lógico completo:
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐ ┌──────────────┐
│ ALUMNO │ │ INSCRIPCION │ │ CURSO │ │ PROFESOR │
├──────────────┤ ├──────────────────┤ ├──────────────┤ ├──────────────┤
│ # id_alumno │──┼<│ * fecha │>┼─│ # codigo │>- │ # id_profesor│
│ * nombre │ │ o nota │ │ * nombre │ └─│ * nombre │
│ * email │ └──────────────────┘ │ * horas │ │ * email │
│ o telefono │ │ o descripcion│ │ o telefono │
└──────────────┘ └──────────────┘ └──────────────┘El resultado son cuatro tablas:
CREATE TABLE profesor (
id_profesor NUMBER PRIMARY KEY,
nombre VARCHAR2(100) NOT NULL,
email VARCHAR2(150) NOT NULL,
telefono VARCHAR2(30)
);
CREATE TABLE alumno (
id_alumno NUMBER PRIMARY KEY,
nombre VARCHAR2(100) NOT NULL,
email VARCHAR2(150) NOT NULL,
telefono VARCHAR2(30)
);
CREATE TABLE curso (
codigo VARCHAR2(10) PRIMARY KEY,
nombre VARCHAR2(100) NOT NULL,
horas NUMBER NOT NULL,
descripcion VARCHAR2(500),
id_profesor NUMBER NOT NULL,
CONSTRAINT fk_curso_profesor
FOREIGN KEY (id_profesor) REFERENCES profesor (id_profesor)
);
CREATE TABLE inscripcion (
id_alumno NUMBER NOT NULL,
codigo VARCHAR2(10) NOT NULL,
fecha DATE NOT NULL,
nota NUMBER(3,1),
CONSTRAINT pk_inscripcion PRIMARY KEY (id_alumno, codigo),
CONSTRAINT fk_insc_alumno
FOREIGN KEY (id_alumno) REFERENCES alumno (id_alumno),
CONSTRAINT fk_insc_curso
FOREIGN KEY (codigo) REFERENCES curso (codigo)
);Recorre el script comparándolo con el diagrama y vas a ver que cada línea viene de un símbolo:
- Cada
#se convirtió en unPRIMARY KEY. - Cada
*se convirtió en unNOT NULL. - Cada
oes una columna sinNOT NULL—telefono,descripcion,nota. - La relación PROFESOR→CURSO puso
id_profesorencurso, del lado de la pata de gallo, yNOT NULLporque ese extremo era continuo. - El UID barrado de INSCRIPCION se convirtió en
PRIMARY KEY (id_alumno, codigo), que es lo que impide inscribir dos veces al mismo alumno en el mismo curso.
El orden de creación importa: curso referencia a profesor, así que profesor tiene que existir antes. Data Modeler ordena el script solo. Si escribes el SQL a mano, la regla es crear primero las tablas sin claves foráneas y después las que dependen de ellas.
Los tipos de dato
El modelo lógico no habla de tipos: ese es el salto al modelo físico, donde recién importa qué motor se va a usar. Los más habituales en Oracle:
| Dato | Tipo | Nota |
|---|---|---|
| Texto de largo variable | VARCHAR2(n) | En otros motores es VARCHAR; el 2 es propio de Oracle |
| Número entero | NUMBER o NUMBER(p) | |
| Número con decimales | NUMBER(p, d) | NUMBER(3,1) da hasta 99.9 — sirve para una nota |
| Fecha | DATE | En Oracle incluye la hora |
| Fecha con precisión | TIMESTAMP | |
| Sí/No | NUMBER(1) o CHAR(1) | Oracle no tiene un tipo booleano en SQL |
Elegir el largo de un VARCHAR2 de menos es un problema real y recurrente: VARCHAR2(50) para un email parece de sobra hasta que llega uno más largo y el INSERT falla. Y elegir un tipo numérico para algo que no se usa para calcular —un teléfono, un código postal— rompe los que empiezan con cero o tienen guiones. Si no vas a sumarlo, es texto.
La regla de integridad referencial
Una vez creadas las claves foráneas, la base de datos empieza a hacer cumplir sola algo llamado integridad referencial: no puede existir una fila que apunte a algo que no existe.
INSERT INTO curso VALUES ('JAV-01', 'Java Básico', 40, NULL, 99);Si no hay ningún profesor con id_profesor = 99, la base rechaza el INSERT:
ORA-02291: integrity constraint (FK_CURSO_PROFESOR) violated - parent key not foundY en el otro sentido, borrar un profesor que dicta cursos también se rechaza (ORA-02292), porque dejaría cursos apuntando a la nada. Eso es exactamente lo que el modelo prometía cuando dibujaste esa línea continua: la base ahora lo garantiza, sin depender de que ningún programa se acuerde de controlarlo.
Esta es la razón de fondo por la que vale la pena modelar. Las restricciones que dibujaste —#, *, las relaciones— se convierten en garantías que la base hace cumplir para todos los programas que la usen: el sistema web, el script de importación, y el que alguien escriba dentro de tres años. Un control escrito en el código de una aplicación solo protege a esa aplicación.
Hacerlo en Data Modeler
Todo esto en la herramienta es un botón. Con el modelo lógico terminado:
- En la barra de herramientas, Engineer to Relational Model (la flecha que apunta a la derecha), o menú File → Engineer to Relational Model.
- Se abre un diálogo con la lista de lo que se va a generar; se aceptan las opciones por defecto y Engineer.
- Aparece el modelo relacional en la pestaña Relational Models: las mismas cajas, ahora con PK y FK marcadas.
- Para el SQL: menú File → Export → DDL File, se elige el motor y se genera el script.
La flecha contraria también existe: partiendo de un modelo relacional, se puede hacer ingeniería inversa hacia el lógico — útil cuando heredas una base ya creada y quieres entender su diseño.
Pon a prueba
DEPARTAMENTO EMPLEADO
┌──────────────┐ ┌──────────────┐
│ # id_depto │- - - - - - - - - - -<│ # id_empleado│
│ * nombre │ │ * nombre │
└──────────────┘ └──────────────┘La entidad tiene # id_socio, * nombre y o telefono. Escribe las restricciones que corresponden a cada símbolo.
CREATE TABLE socio (
id_socio NUMBER ,
nombre VARCHAR2(100) ,
telefono VARCHAR2(30)
);A partir de este modelo, escribe las tres sentencias CREATE TABLE.
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ SOCIO │ │ PRESTAMO │ │ LIBRO │
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ # id_socio │──┼<│ # fecha_retiro │>┼──│ # isbn │
│ * nombre │ │ o fecha_devol │ │ * titulo │
│ o email │ └──────────────────┘ │ o autor │
└──────────────────┘ └──────────────────┘CREATE TABLE socio (
id_socio NUMBER PRIMARY KEY,
nombre VARCHAR2(100) NOT NULL,
email VARCHAR2(150)
);
CREATE TABLE libro (
isbn VARCHAR2(20) PRIMARY KEY,
titulo VARCHAR2(200) NOT NULL,
autor VARCHAR2(100)
);
CREATE TABLE prestamo (
id_socio NUMBER NOT NULL,
isbn VARCHAR2(20) NOT NULL,
fecha_retiro DATE NOT NULL,
fecha_devol DATE,
CONSTRAINT pk_prestamo
PRIMARY KEY (id_socio, isbn, fecha_retiro),
CONSTRAINT fk_prestamo_socio
FOREIGN KEY (id_socio) REFERENCES socio (id_socio),
CONSTRAINT fk_prestamo_libro
FOREIGN KEY (isbn) REFERENCES libro (isbn)
);Lo que hay que notar:
- La PK de
prestamotiene tres columnas, no dos: el UID barrado aportaid_socioeisbn, yfecha_retirollevaba su propio#. Sin la fecha, el mismo socio no podría volver a pedir el mismo libro nunca más. fecha_devolno llevaNOT NULLporque erao, y eso es justamente lo que representa un préstamo todavía sin devolver. PreguntarWHERE fecha_devol IS NULLda la lista de libros que están afuera.isbnesVARCHAR2, noNUMBER, aunque parezca un número: tiene guiones, puede empezar con cero y nadie lo suma nunca. Es la regla de “si no vas a calcular con él, es texto”.- Las columnas de la PK compuesta son
NOT NULLexplícitamente. Formar parte de una clave primaria ya lo implica, pero escribirlo hace evidente de dónde salió cada*del diagrama.
Siguiente paso
Ya recorriste el camino completo, de un párrafo en palabras a un script SQL. Falta hacerlo con la herramienta delante, clic por clic — ver Tu primer diagrama en Data Modeler — y después practicar con el banco de ejercicios.