Respondiendo a Gallosuarez, claro que se pueden manejar la integridad referencial via triggers pero implica el que se definan indices para asuntos de rendimiento.
Hay algunos casos en los que ciertas tablas no son buena idea crearlas aún en aras de la flexibilidad en la parametrización de aplicaciones, por ejemplo, crear una tabla para "estado civil" sabiendo que los valores para dicha tabla son pocos y dificlmente cambian. Algunos desarrolladores crean la tabla "estado civil" y crean una llave foranea para el campo"empleado"."estado_civil".
Hay un tema que se llama la "selectividad", lo ideal es que por cada valor clave de la tabla maestro haya una distribución de registros en la tabla detalle, por ejemplo, un campo "empleado"."codigo_ciudad" podría tener esa distribución, pero un campo "cliente"."sexo" no lo tendria porque por cada sexo ('M'masculino,'F'emenino, 'I'ndeterminado (se presentan algunos casos donde al bebe no se le puede dictaminar su sexo, caso hermafrodita)) la distribucion de registros es 50% y 50%, una llave foránea en este caso puede ser una mala decisión y consultas cuya clausula where hagan referencia a este campo no serían tan eficientes.
Una solución es crear una tabla para conservar este tipo de valores, en algunos casos utilizo algo como esto:
Código SQL
[-]
CREATE TABLE TS_LISTA_VALORES (
NOMBRE_CAMPO VARCHAR(40) NOT NULL,
VALOR_CORTO VARCHAR(40) NOT NULL,
VALOR_LARGO VARCHAR(255)
);
y se crean triggers para el caso "cliente"."sexo" que verifiquen la existencia de valores en esa tabla. En lo personal, si una lista de valores que puede contener un campo es menor o igual que 20 datos los mando a la tabla anterior, si es mayor de 20 lo mando a una tabla y le creosu respectiva llave foránea.
Para ampliar la cosa tendriamos que irnos al campo de las desnormalizaciones pero saldriamos del interes de este hilo.