04. Restricciones de integridad

La definición de relación como un conjunto de tuplas de un cierto esquema, permite la existencia de relaciones que no representan estados válidos del mundo real que se está representando.

Ejemplo 2.25

Sea el siguiente esquema relacional:

Empleado(dni: dom_dni, nombre: dom_nom, dirección: dom_dir, no_emp: dom_no, dep: dom_dep)

Departamento(cod_dep: dom_dep, descripción: dom_des)

dónde el atributo no_emp de la relación Empleado es el número del empleado en la empresa, y el atributo dep de la relación Empleado indica a qué departamento está asignado, en caso de estarlo.

Una posible extensión de estas relaciones se representa en las siguientes tablas:

Asumiendo la interpretación obvia de este esquema, la extensión de la relación Empleado no es “válida” por los motivos siguientes:

  • hay dos empleados con el mismo dni,

  • hay dos empleados con el mismo número de trabajador,

  • hay un empleado sin nombre, y

  • aparece una referencia a un departamento que no existe en la relación Departamento.

Para evitar estos problemas y aumentar la capacidad expresiva del Modelo Relacional, éste se extiende incorporando el concepto de restricción de integridad.

Una restricción de integridad representa una propiedad del mundo real del cual la base de datos es una representación.

Las restricciones de integridad se especifican en el esquema de la base de datos y se deben cumplir en cualquier extensión del mismo; el responsable de que esto sea así es el sistema de gestión de bases de datos que debe velar en todo momento por la integridad de la información almacenada.

En el Modelo Relacional se contemplan cuatro tipos de restricciones:

  • restricción de unicidad

  • restricción de valor no nulo

  • restricción de clave primaria

  • restricción de integridad referencial.

Ejemplo 2.26

Estos cuatro tipos de restricciones presentados, permitirían definir sobre el esquema del Ejemplo 2.25 las siguientes propiedades observables en el mundo real:

  • el atributo dni identifica unívocamente a un empleado (restricción de unicidad).

  • el atributo no_emp también identifica unívocamente a un empleado y es utilizado internamente en la organización para esa función (restricción de clave primaria).

  • de todo empleado se debe conocer al menos el valor de los atributos nombre, dni y no_emp (restricción de valor no nulo).

  • de todo departamento se debe conocer al menos el valor de los atributos cod_dep y descripción (restricción de valor no nulo).

  • el atributo cod_dep identifica univocamente a un departamento y es utilizado internamente en la organización para hacer esa función. (restricción de clave primaria).

  • todos los departamentos referidos desde el atributo dep de Empleado deben aparecer en la relación Departamento (restricción de integridad referencial).

En los apartados siguientes se presentan con detalle estos cuatro tipos de restricciones, para ello sean:

  • R una relación de esquema ρ = {(A1, D1), (A2, D2),..., (An, Dn)}

  • S una relación de esquema σ = {(B1, E1), (B2, E2),..., (Bm, Em)}

  • Aρ el conjunto de nombres de atributo de ρ, Aρ = {A1, A2, ..., An}

  • Aσ el conjunto de nombres de atributo de σ, Aσ = {B1, B2, ..., Bm}

Restricción de valor no nulo

La definición de una restricción de valor no nulo sobre un conjunto de atributos K de la relación R expresa la siguiente propiedad: “no debe haber en R una tupla que tenga el valor nulo en algún atributo de K”.

Formalmente, dado un conjunto de atributos K (K ⊆ Aρ y K ≠ ∅), se dice que R satisface una restricción de valor no nulo sobre K si y sólo si se cumple la siguiente propiedad: ∀t (t ∈ R → ¬ ∃Ai (Ai ∈ K ∧ nulo t(Ai))) en caso contrario R viola esta restricción.

Ejemplo 2.27

En el Ejemplo 2.25 los atributos que deberían definirse con restricción de valor no nulo son: dni, no_emp y nombre de la relación Empleado, y cod_dep y descripción de la relación Departamento.

Hay que destacar que la definición de esta propiedad para un atributo es una decisión del diseñador. En este ejemplo se ha considerado que se debe conocer el nombre de cada empleado así como su dni y el número que se le asigna permitiéndose, sin embargo, el desconocimiento de su dirección o del departamento al que está adscrito aunque, en este último caso, podría interpretarse este desconocimiento (el valor nulo) como el hecho de que ese empleado no está, de momento, asignado a ningún departamento.

Restricción de unicidad

La definición de una restricción de unicidad sobre un conjunto de atributos K de la relación R expresa la siguiente propiedad: “no debe haber en R dos tuplas que tengan el mismo valor en todos los atributos del conjunto K”.

Formalmente, dado un conjunto de atributos K (K ⊆ Aρ y K ≠ ∅), se dice que R satisface una restricción de unicidad sobre K si y sólo si se cumple la siguiente propiedad: ¬∃t1 ∃t2 (t1 ∈ R ∧ t2 ∈ R ∧ t1 ≠ t2 ∧ ∀Ai (Ai ∈ K → t1(Ai) = t2 (Ai) )) en caso contrario R viola esta restricción.

Ejemplo 2.28

En el Ejemplo 2.25, y con el significado obvio, para ambos esquemas de relación, los atributos que deberían definirse con restricción de unicidad son: dni y no_emp de la relación Empleado y cod_dep de la relación Departamento.

Concepto de clave primaria. Integridad de clave primaria

Como ya se ha comentado en otras ocasiones, la definición de relación como conjunto de tuplas significa que en una relación no existen dos tuplas iguales; es decir que cualquier tupla es distinta de cualquier otra y que, por lo tanto es identificable de forma única dando los valores de sus atributos. Aunque este hecho asegura la posibilidad de identificar y seleccionar las tuplas de una relación con el objeto de poder manipularlas, evidentemente no parece la forma más cómoda de hacerlo, ya que generalmente existen en las relaciones subconjuntos de atributos que debido a su significado pueden asegurar también esta identificación única. Por ello y con el fin de facilitar la manipulación de las selecciones en una base de datos relacional, se introduce en el Modelo el concepto de clave primaria. Una clave primaria de una relación es un conjunto de atributos de su esquema que son elegidos para servir de identificador unívoco de sus tuplas. Evidentemente para que la clave primaria cumpla con esta función de identificación deberá cumplir ciertos requisitos: sus atributos deberán tener siempre un valor para cada tupla (es decir no podrán tomar valores nulos) y además este valor deberá ser único en cada tupla. Además y como orientación de diseño, para que la clave sea cómoda y manejable se debe elegir una clave que sea minimal, en el sentido de que todos los atributos que la forman son necesarios para la función identificadora. A continuación se presenta formalmente el concepto de clave primaria.

Dado un conjunto de atributos CP (CP ⊆ Aρ y CP ≠ ∅) que se ha definido como clave primaria de R, se dice que R satisface la restricción de integridad de clave primaria si se cumplen las siguientes propiedades:

a) R satisface una restricción de valor no nulo sobre CP, y b) R satisface una restricción de unicidad sobre CP en caso contrario R viola esta restricción.

Además, es aconsejable que CP sea minimal, es decir que no tenga ningún subconjunto propio que pueda ser a su vez clave primaria de R.

La clave primaria de una relación siempre es única, si hay más de un subconjunto de atributos que cumpla las propiedades exigidas se elige uno de ellos y a los restantes se les especifican las propiedades anteriores (unicidad y valor no nulo) pero sin otorgarles la categoría de clave primaria.

La importancia de la clave primaria de una relación reside en el hecho de que, como ya se ha dicho, proporciona una identificación unívoca de sus tuplas, ya que si CP es la clave primaria de R, entonces toda tupla de R contiene siempre un valor de CP distinto al de cualquier otra tupla y distinto del valor nulo; este hecho justifica que para hacer referencia a una tupla de una relación desde otra relación se utilice, usualmente, el valor de la clave primaria de la tupla, por este motivo es recomendable que en la definición de las relaciones se especifique siempre el conjunto de atributos que forman la clave primaria.

Ejemplo 2.29

En las relaciones del Ejemplo 2.25 se tienen las siguientes claves primarias:

  • En la relación Empleado hay dos posibles claves primarias, los conjuntos {dni} y {no_emp} ya que ambos cumplen las condiciones exigidas. En este caso parece más razonable elegir como clave primaria {n_emp} ya que, en el contexto de una empresa es razonable que este atributo sea el identificador natural para sus trabajadores.

  • En la relación Departamento la clave primaria es el conjunto {cod_dep}.

Concepto de clave ajena. Integridad referencial

Las claves ajenas son el mecanismo que proporciona el Modelo Relacional para expresar asociaciones entre los objetos representados por las relaciones del esquema de la base de datos. La forma de hacerlo consiste en incluir en el esquema de una relación R atributos identificadores de otra relación S, a este conjunto de atributos se le conoce como clave ajena de la relación R que hace referencia a la relación S. Para que la clave ajena cumpla su función de referencia se debe asegurar que los valores que toman sus atributos en las tuplas de R aparecen en alguna tupla de S.

Este concepto se presenta formalmente a continuación.

Dada una clave ajena CA de R que hace referencia a S y que se define como:

  1. un subconjunto K = {Ai, Aj,..., Ak}(K ⊆ Aρ y K ≠ ∅),

  2. una biyección f: K→ J tal que:

    • J ⊆ Aσ,

    • J tiene restricción de unicidad, y

    • ∀Ai (Ai ∈ K→ Ai y f(Ai) tienen el mismo dominio, y

  3. un tipo de integridad referencial que puede ser: débil, parcial o completa

se dice que R satisface la restricción de integridad referencial sobre CA si, según el tipo elegido, se cumple la propiedad que se especifica:

  • Integridad referencial débil:

∀t (t ∈ R → (∃Ai (Ai ∈ K ∧ nulo( t(Ai)) )

o

∃m (m ∈ S ∧ ∀Ai (Ai ∈ K → t(Ai) = m(f(Ai))))))

  • Integridad referencial parcial:

∀t (t ∈ R → (∀Ai (Ai ∈ K → nulo( t(Ai)) )

o

∃m (m∈S y ∀Ai((Ai ∈ K ∧ ┐nulo( t(Ai) ) → t(Ai) = m(f(Ai)))))))

  • Integridad referencial completa:

∀t (t ∈ R → (∀Ai (Ai ∈ K → nulo( t(Ai)))

o

∃m (m∈S y ∀Ai((Ai ∈ K → ┐nulo( t(Ai) ) ∧ t(Ai) = m(f(Ai)))))))

en caso contrario R viola esta restricción.

A pesar de que, en general, el lenguaje natural es ambiguo y en este caso no resulta sencillo dar una explicación de estas propiedades, para relajar su presentación formal, a continuación se enuncian los tres tipos de integridad referencial de forma intuitiva:

  • Integridad referencial débil: si en una tupla de R todos los valores de los atributos de K tienen un valor que no es nulo, entonces debe existir una tupla en S que tome esos mismos valores en los atributos de J;

  • Integridad referencial parcial: si en una tupla de R algún atributo de CA tiene un valor que no es nulo, entonces debe existir una tupla en S que tenga en los atributos de J los mismos valores que los atributos de CA que no son nulos; y

  • Integridad referencial completa: en una tupla de R todos los atributos de CA deben tener valor nulo o bien todos tienen un valor que no es nulo y entonces debe existir una tupla en S que tome en los atributos de J los mismos valores que los atributos de CA.

Cuando se define una clave ajena en una relación se debe especificar el tipo de integridad referencial que se exige excepto en el caso de que la clave ajena conste sólo de un atributo o cuando todos ellos sufran restricción de valor no nulo ya que en estos dos casos los tres tipos de integridad referencial coinciden.

Ejemplo 2.30

En el Ejemplo 2.8 los atributos rcod y pcod de la relación Pasa_por constituyen dos claves ajenas; rcod hace referencia a la relación Río y pcod a la relación Provincia de forma que, como deben cumplir la integridad referencial, nunca aparecerán valores que no se correspondan con los códigos de un río y de una provincia existentes en la base de datos.

En el Ejemplo 2.25 el atributo dep de la relación Empleado es una clave ajena a la relación Departamento de forma que nunca podrá haber un empleado asignado a un departamento que no aparezca en la relación Departamento.

Ejemplo 2.31

Sea el siguiente esquema relacional referente a una agencia de viajes especializada en organizar visitas culturales a distintas ciudades del mundo. La relación Viaje contiene la programación actual de viajes; la relación Oferta_Guía contiene información sobre la disponibilidad de guías para las distintas ciudades; la relación Guía contiene los datos personales de los guías con los que trabaja la agencia; por último, la relación Ciudad contiene información sobre las ciudades.

Viaje(código:dom_código, destino: dom_ciudad, guía: dom_agente, fecha: dom_fecha)

CP:{código}

CAj:{destino, guía} → Oferta_Guía f(destino)=ciudad f(guía)=dni

Oferta_Guía(ciudad: dom_ciudad, guía: dom_agente)

CP:{ciudad, guía}

CAj:{guía} → Guía f(guía)=dni

CAj:{ciudad}→ Ciudad f(ciudad)=nombre

Guía(dni: dom_agente, nombre: dom_nombre)

CP:{dni}

Ciudad(nombre: dom_ciudad, país: dom_país)

CP:{nombre}

Dado que la clave ajena de Viaje puede ser parcialmente nula es necesario indicar el tipo de integridad referencial que se exige. Para entender mejor la implicación que tiene le elección de un tipo de integridad referencial para esta clave ajena, sea la siguiente extensión de la base de datos. En el ejemplo se han considerado viajes en los que el atributo guía tiene valor nulo. La misma reflexión podría haberse hecho respecto al atributo destino.

Sea cual sea el tipo de integridad referencial elegido, las tres primeras tuplas de la relación Viaje tienen el mismo tratamiento:

  • El viaje v1 es válido. Es decir la inserción de esta tupla sería aceptada por el SGBD ya que la clave ajena tiene un valor que no es nulo en todos sus atributos, {destino} = 'Valencia' y {guía }=1, y existe una tupla en Oferta_ guía con esos valores en ciudad y guía respectivamente.

  • El viaje v2 no es válido ya que la clave ajena tiene un valor que no es nulo en todos sus atributos {destino} ='Cáceres' y {guía}=2 y no existe ninguna tupla en Oferta_guía con esos valores en ciudad y guía respectivamente.

  • El viaje v3 también es valido en los tres tipos de integridad referencial porque la clave ajena tiene valor nulo en todos sus atributos.

En los demás viajes, la clave ajena es parcialmente nula por lo que los viajes serán válidos o no según el tipo de integridad referencial elegido:

Integridad referencial débil: Los viajes v4 y v5 son ambos válidos ya que en este tipo de integridad sólo se realiza la comprobación cuando la clave ajena (todos sus atributos) tiene un valor que no es nulo. Obsérvese que el viaje v5 sí que es válido pese a que la ciudad (Qbvfb) ni siquiera coinciden con el nombre de una ciudad en la relación Ciudad.

Integridad referencial parcial: En este caso, la comprobación se realiza aunque la clave ajena no esté completa, es decir para los atributos de la misma que no tienen valor nulo. Así, el viaje v4 es válido ya que aunque aún no se conoce el guía responsable, la ciudad de destino aparece en al menos una tupla de la relación Oferta_guía. Sin embargo, el viaje v5 no es válido porque su destino (Abvfb) no aparece en ninguna tupla de la relación Oferta_guía.

Integridad referencial completa: En este caso no está permitido que la clave ajena sea parcialmente nula por lo que los viajes v4 y v5 no son válidos.

Hay que darse cuenta de que la elección de un tipo de integridad referencial para la clave ajena de la relación Viaje permite reflejar distintos tipos de funcionamiento interno de la organización. Si el tipo elegido es débil, sólo cuando se conocen el destino y el guía del viaje se hace la comprobación de que el guía es adecuado para la ciudad de destino; en cualquier otro caso, no se realiza ninguna comprobación, es decir se puede dar de alta un viaje sin conocer alguno de estos datos. Si el tipo elegido es parcial, entonces se puede dar de alta un viaje sin dar sus datos completos (destino y guía), pero cuando alguno de ellos se conoce, se comprueba su aparición en la relación Oferta_guía, lo que signfica que si esta comprobación es válida entonces el viaje es posible. Por último, si el tipo elegido es completa, entonces o se conoce el destino y el guía del viaje comprobándose su validez en Oferta_guía o no se puede dar de alta el viaje.

Restauración de la integridad referencial: directrices al SGBD

La comprobación de las restricciones es competencia del SGBD que debe asegurar que la base de datos después de cada actualización permanece íntegra, es decir que satisface todas las restricciones definidas en su esquema.

Generalmente, ante una actualización de la base de datos que viola una restricción, el SGBD suele rechazar la actualización devolviendo la base de datos al estado anterior. En algunos casos, sin embargo, sería deseable un comportamiento menos rígido que permitiera al usuario incluir en el esquema de la base de datos qué debe hacer el sistema cuando se detecte la violación de una restricción. Esta posibilidad está contemplada en el Modelo Relacional para la restricción de integridad referencial. En este caso, además de conocer los atributos que constituyen la clave ajena y los atributos referidos por ella, se le puede indicar al SGBD la directriz asociada a esa clave ajena, es decir, el comportamiento del sistema frente a actualizaciones de la base de datos que violen esa integridad referencial. A continuación se presentan estas ideas con más detalle.

Supóngase que existen dos relaciones R y S tal que R tiene una clave ajena CA que hace referencia a un conjunto J de atributos de S. El SGBD debe preservar la integridad referencial representada por esta clave ajena, por lo tanto debe comprobar la restricción frente a actualizaciones del usuario sobre las relaciones R y S que puedan violar la integridad. Estas actualizaciones son: inserciones y modificaciones de CA en R, y borrados y modificaciones de J en S.

Frente a las actualizaciones sobre la relación R que violen la integridad referencial, éstas serán siempre rechazadas por el SGBD ya que no hay ninguna acción compensatoria que pueda restaurar la integridad y que sea razonable.

Frente a las actualizaciones sobre la relación S que violen la integridad referencial, éstas podrán ser rechazadas por el SGBD o podrán ser aceptadas por éste que, en este caso, deberá tomar las acciones adecuadas para restaurar la integridad. Estas acciones pueden ser alguna de las siguientes:

  • frente a borrados de tuplas de S: supóngase que se desea borrar una tupla σ de S a la que hace referencia una tupla ρ de R, esto es, que ρ(CA)=σ(J). Existen dos posibles acciones a realizar por el SGBD:

    1. realizar el borrado de σ y modificar el valor de CA a valor nulo en toda tupla ρ que hace referencia a σ (borrado a nulos), o

    2. realizar el borrado de σ y el borrado de toda tupla ρ que haga referencia a σ (borrado en cascada).

  • frente a modificaciones de los valores de J en tuplas de S: supóngase que se desea modificar el valor de J en la tupla σ de S a la que hace referencia la tupla ρ de R, esto es que ρ(CA)=σ(J). Existen dos posibles comportamientos a seguir por el SGBD:

    1. realizar esta modificación del valor de J en σ y actualizar el valor de CA a valor nulo en toda tupla ρ que haga referencia a σ (modificación a nulos), o

    2. realizar la modificación de σ y desencadenar la modificación de cada tupla ρ que hacía referencia a σ, actualizando el valor en CA por el nuevo valor modificado en σ (modificación en cascada).

Cuando se desee que el SGBD aplique alguna de estas acciones restauradoras será necesario indicárselo mediante la inclusión de una directriz en la definición de la clave ajena.

Ejemplo 2.32

Sea la siguiente base de datos del esquema presentado en el Ejemplo 2.25:

Empleado

no_emp                 dni             nombre             dirección                 dep

    1                 20.450.120     Juan Pérez     Cuenca 20                 d1

    2                 12.904.569     José Abad      Blasco Ibáñez 35      d2

    3                 35.784.843     María Gutiérrez         ?                        d2

    4                 12.345.678     Pepa Gómez     Colón 15                  d2

    5                 11.345.678     Ana Orts             Cuenca 20               d3

Departamento

cod_dep             descripción

    d1                         Ventas

    d2                         Compras

    d3                         Contabilidad

En la que como ya se ha dicho el atributo dep de la relación Empleado es una clave ajena que hace referencia al atributo cod_dep de la relación Departamento y que por lo tanto debe cumplir la integridad referencial que en este caso concreto se enunciaría de la forma siguiente: “Todo valor distinto del nulo que aparezca en el atributo dep de la relación Empleado debe aparecer en el atributo cod_dep de la relación Departamento”. Algunas operaciones que violan la integridad referencial son las siguientes:

  • Insertar(Empleado, {(dni: 15.236.333), (nombre: “Pepe Pérez”), (dirección: ?), (no_emp: 7), (dep: d8)})

Esta operación será rechazada por el SGBD

  • Borrar(Departamento, {(cod_dep: d2), (descripción, “Compras”)})

Según la definición de la calve ajena en el esquema de la base de datos, esta operación puede ser rechazada (si no se indica directriz) o aceptada pudiéndose generar dos resultados distintos según la directriz restauradora elegida:

  1. Directriz: BORRADO EN CASCADA, en este caso se borra el departamento deseado y todos los empleados que estaban asignados a él quedando la base de datos como se muestra:
  2. Directriz: BORRADO A NULOS, en este caso se borra el departamento deseado y los empleados que trabajaban en él pasan a no tener departamento de adscripción (se asigna el valor nulo a su atributo dep).

Otras restricciones de integridad

Dada la diversidad de sistemas de información, existen propiedades que no se pueden expresar con los cuatro tipos de restricciones de integridad proporcionados en el Modelo. Para expresar estas propiedades se puede utilizar, durante el diseño de la base de datos, un lenguaje de tipo lógico como el que se ha mostrado en el apartado 2.3.2. Como se verá más adelante, en el apartado 2.5.1 y en las prácticas de laboratorio, en el lenguaje estándar SQL, exiten sentencias que permiten definir cualquier restricción de integridad de forma declarativa, es decir por medio de una fórmula lógica.

Ejemplo 2.33

Supongamos que en la empresa representada en el Ejemplo 2.25 debe cumplirse la siguiente restricción de integridad: “Todo departamento debe tener asignado al menos un empleado”. Esta propiedad no es de ninguno de los tipos de restricicones contemplados en el Modelo. Para dejar documentada esta restricción, se puede utilizar un lenguaje lógico del tipo usado en el apartado 2.3.2:

∀DX: Departamento (∃EX: Empleado (DX.cod_dep = EX.dep))

Resumiendo lo visto en este apartado 2.4, la definición de una relación en el esquema de la base de datos, se puede enriquecer con la definición de los siguientes tipos de restricciones:

  • VNN: restricción de valor no nulo,

  • UNI: restricción de unicidad,

  • CAj: clave ajena (incluyendo directriz de restauración de la integridad),

  • CP: clave primaria, y

  • restricciones de integridad generales.

El siguiente esquema relacional genérico ilustra la notación para representar estos tipos de restricciones en el esquema:

R(A1:D1, A2:D2,..., Ar:Dr)

    CP: {Ai,..., Am}

    CAj: {Ao,..., Ap} → S

        f (Ao )= Bj

        ...

        f( Ap )= Bn

            [BORRADO {CASCADA | NULOS}]

            [MODIFICACIÓN {CASCADA | NULOS}]

S(B1:E1, B2:E2,..., Bt:Et)

    CP: {Bj,..., Bn}

    UNI: {Bq,..., Br}

    VNN: {Bs,..., Bt}

Ejemplo 2.34

La definición completa del esquema del Ejemplo 2.25 incluyendo todas las restricciones que se han mencionado sería la siguiente:

Empleado(dni: dom_dni, nombre: dom_nom, dirección: dom_dir, no_emp: dom_no, dep: dom_dep)

    CP: {no_emp}

    UNI: {dni}

    VNN: {nombre}

    CAj: {dep} → Departamento f(dep)=cod_dep

Departamento(cod_dep: dom_dep, descripción: dom_des)

    CP: {cod_dep}

    VNN: {descripción}

Restricciones de integridad (generales):

∀DX: Departamento ( ∃EX: Empleado (Empleado(EX) ∧ DX.cod_emp = EX.dep))