Si hasta ahora no has usado a las Foreign Keys podrías pensar que son innecesarias o que podrías muy bien manejarte sin ellas. Bien, es posible vivir sin usarlas, pero si las usas ganarás muchísimo en confiabilidad y en seguridad de tus datos.

Una Foreign Key sirve para relacionar a dos tablas entre sí.

Una de las tablas es la tabla «padre» y la otra tabla es la tabla «hija».

En la tabla «hija» solamente pueden introducirse valores que ya existan en la tabla «padre».

El Firebird no te permitirá que en la tabla «hija» coloques un valor que no existe en la tabla «padre». Y eso es muy bueno porque evita que tengas filas «huérfanas». Se llama «fila huérfana» a una fila que no tiene «padre».

Si pudieras tener filas huérfanas podrían darse estos casos:

  • Durante el año le cobraste varias veces a un cliente, luego borraste los datos de ese cliente, y la pregunta es: ¿a quién cornos le cobraste si no encuentras su nombre por ningún lado?
  • Durante el año vendiste varias veces un producto, luego borraste los datos de ese producto, y la pregunta es: ¿cuál producto vendiste si su nombre no puedes encontrar por ningún lado?
  • Etc., etc., etc.

Desde luego que podrías obtener esa información mirando informes antiguos, o restaurando backups, o quizás estrujando tu cerebro para hallarla en tu memoria pero el problema es que en esos casos la información que necesitas estará afuera de tu Base de Datos. Y eso es un error gravísimo.

Porque la información necesaria siempre debería estar adentro de tu Base de Datos.

Cuando relacionas dos tablas entre sí usando una Foreign Key siempre el valor que insertas en la tabla «hija» ya existe en la tabla «padre». Ok, hasta ahí muy bien, pero ¿qué ocurre si se quiere borrar o modificar una fila de la tabla padre que tiene filas relacionadas en la tabla «hija»?

Por ejemplo, queremos borrar los datos de un cliente, pero a ese cliente le hemos hecho cobranzas.

El Firebird te ofrece 3 posibilidades:

  1. Impedir
  2. Borrar o modificar también todas las filas relacionadas de la tabla «hija». A esto se le llama «en cascada».
  3. Poner un valor NULL o el valor por defecto en las filas de la tabla «hija»

La opción más común, la más usual, la más normal, la generalmente más correcta es la 1. O sea, no podrás borrar ni modificar una fila de la tabla «padre» si tiene filas relacionadas en la tabla «hija».

La opción 2. puede ser útil cuando se modifica una fila de la tabla «padre», pero extremadamente peligrosa cuando se la borra. Muchísimo cuidado y muchísima atención ahí. Borrar una fila de la tabla «padre» que no debería haberse borrado puede causar un desastre mayúsculo.

La opción 3. es muy raramente empleada porque en ese caso estarías evitando la ventaja de tener una Foreign Key.

¿Y si se tiene una necesidad legítima de borrar o modificar una fila de la tabla «padre»?

A veces, realmente se necesita borrar una fila de la tabla «padre». Bien, es posible hacerlo. Primero se borran todas las fijas relacionadas de la tabla «hija» (o de las tablas «hijas», porque podrían ser varias) y luego se borra la fila de la tabla «padre».

Como ves, algo así no puede ocurrir «por accidente». Si se hizo todo ese trabajo es porque (se supone) que se sabía muy bien lo que se estaba haciendo. (Si no se sabía, y se hizo una tremenda idiotez, al menos no tendrán excusa)

¿Y siempre hay que relacionar tablas?

No, no siempre. Muchas veces sí es posible hacerlo pero no es conveniente hacerlo.

¿Y cuándo no es conveniente relacionar a dos tablas entre sí?

Cuando la tabla «padre» tiene muy pocas filas y la tabla «hija» tiene muchas filas.

Por ejemplo, una empresa tiene 2 sucursales. Por lo tanto la tabla de SUCURSALES tiene 2 filas. Y en la tabla de VENTAS se guarda el código de una de esas sucursales. En ese caso, es un error hacer la relación mediante una Foreign Key. ¿Y por qué es un error? por algo que en Firebird se llama «selectividad de los índices». Nunca es conveniente relacionar cuando una de las tablas tiene muy pocas filas.

¿Y en ese caso, cómo se evitaría que alguien borre a una Sucursal cuyo código existe en la tabla de VENTAS?

Mediante un trigger BEFORE DELETE de la tabla de SUCURSALES.

IF (EXISTS(SELECT VTC_CODSUC FROM VENTASCAB WHERE VTC_CODSUC = OLD.SUC_CODIGO)) THEN
   EXCEPTION E_Sucursal_En_Uso;

En este caso, antes de borrar una Sucursal se verifica que no se la haya usado en la tabla VENTASCAB. Si se la usó entonces se lanzará una excepción y no será borrada. Desde luego que normalmente habrá muchos IF … THEN, uno por cada tabla donde se guarda el código de la Sucursal.

Por lo tanto, la regla es:

  • Si la tabla «padre» tiene muchas filas, usar una restricción Foreign Key para relacionarla con la tabla «hija» y evitar que la fila «padre» sea borrada o modificada por un «accidente» cometido por un descerebrado.
  • Si la tabla «padre» tiene pocas filas, usar un trigger BEFORE DELETE para evitar que se borre una de sus filas si es que ese valor existe en otra tabla. Y un trigger BEFORE UPDATE para evitar que se modifique su valor.

Artículos relacionados:

Entendiendo a las Foreign Keys

Selectividad de los índices

Entendiendo las excepciones

El índice del blog Firebird21

El foro del blog Firebird21