¿Prefiere la normalización de la base de datos frente a la transparencia del esquema?

10

Ha surgido un nuevo requisito en una antigua base de código, que básicamente permite la comunicación directa (interna) entre dos clases de usuarios que anteriormente no estaban directamente relacionadas (almacenadas en tablas diferentes con un esquema completamente diferente y, lamentablemente, el código apenas es consciente de OO, mucho menos diseñado, por lo que no hay clase principal). Dado que estamos dispuestos a colgar una bolsa en esta configuración anterior que nunca consideró esta funcionalidad, no hay garantía de que no haya colisiones PK; dado el conjunto de datos en uso, está prácticamente garantizado que HAY.

Entonces, la solución parece obvia: mátala con fuego y reescribe todo el desorden de una tabla de mapeo. Obtuve dos instrucciones sobre las posibles formas de implementar el mapa, pero no soy un DBA, por lo que no estoy seguro de si hay algunos pros y contras que me haya perdido.

En aras de aclarar la abstracción, considere tres grupos de datos de usuarios dispares: profesores, administración, estudiantes (No, esta no es una tarea. ¡Promesa!)

Mapeo 1

(professor_id, admin_id y student_id son claves foráneas para sus tablas respectivas)

| mailing_id (KEY) | professor_id | admin_id | student_id | 
-------------------------------------------------------
| 1001             |     NULL     |    87    |  NULL      |
| 1002             |     123      |   NULL   |  NULL      |
| 1003             |     NULL     |   NULL   |  123       |

El +/- a este enfoque parece bastante pesado en los contras:

  • Dos campos "desperdiciados" por fila
  • Viola 2NF
  • Vulnerable para insertar / actualizar anomalías (una fila con solo 0-1 conjunto de campos NULL, por ejemplo)

Sin embargo, los profesionales no carecen de sus propios méritos:

  • El mapeo se puede lograr con una sola búsqueda
  • Determine fácilmente los datos de "fuente" para un usuario determinado desde el mailing_id

A decir verdad, en mi interior, no me gusta esta idea en absoluto.

Mapeo 2

(suponga que MSG_ * son constantes definidas, tipos de enumeración u otro identificador adecuado)

| mailing_id (KEY)  | user_type (UNIQUE1) | internal_id (UNIQUE2)| 
------------------------------------------------------------------
| 1001              | MSG_ADMIN          | 87                    |
| 1002              | MSG_PROF           | 123                   |
| 1003              | MSG_STUDENT        | 123                   |

Con esta configuración, y un índice compuesto único de {user_type, internal_id} las cosas se vuelven mucho más limpias, se mantiene 3NF y el código de la aplicación no tiene que verificar las anomalías de E / S.

En el lado negativo, hay una pequeña pérdida de transparencia al determinar las tablas de origen del usuario que deben manejarse fuera de la base de datos, lo que básicamente equivale a una asignación a nivel de aplicación de los valores de tipo de usuario a las tablas. En este momento, me estoy inclinando (bastante fuerte) hacia este segundo mapeo, ya que la desventaja es bastante menor.

PERO soy dolorosamente consciente de mis propias limitaciones, y estoy seguro de que probablemente haya perdido ventajas o tropiezos en ambas direcciones, así que recurro a mentes más sabias que las mías.

GeminiDomino
fuente
2
Puede encontrar las ideas de Martin Fowler sobre roles como una lectura interesante.
Marjan Venema el
Fue, de hecho, interesante. Lamentablemente, no tengo demasiada información sobre mi problema específico
GeminiDomino
Obtendrá profesores que se convertirán en administradores y estudiantes que obtendrán empleos en la administración o incluso volverán 10 años después como docentes. Probablemente ya los tengas. ¿Vas a mantenerlos separados o tratarás de unificar?
Elin
Los roles son solo ejemplos, pero entiendo tu punto. En la práctica, incluso si los usuarios cambiaran roles, permanecerían como registros separados de todos modos.
GeminiDomino
Sería genial si reformularas el primer párrafo. No está nada claro. Quiero decir, es obvio que hay un problema, pero no está lo suficientemente claro.
Tulains Córdova

Respuestas:

1

Tu segunda idea es la correcta. Este enfoque le permite hacer todo el mapeo que necesita hacer para integrar sus tres espacios clave en colisión.

Es importante destacar que permite que la base de datos imponga la mayor parte de la coherencia que necesita tener usando restricciones declarativas .

Ya tiene más código del que desea tener, así que no agregue más código del necesario para mantener la coherencia de su lista de claves integrada. Deje que su motor de base de datos haga lo que fue creado para hacer.

El "niño problemático" que le produce molestias en Mapping 2 es la USER_TYPEcolumna. Esta columna es importante porque la necesita para asegurarse de que INTERNAL_IDsolo aparezca como máximo una vez por tipo de usuario. La única vez que necesita un código que sea consciente USER_TYPEes el código que se inserta y elimina de su tabla de mapeo. Esto se puede localizar bastante bien. Supongo que creará un único punto en su código donde se mantiene el contenido de la tabla de mapeo. Una columna adicional en este lugar donde se escriben los datos no es gran cosa. Lo que realmente desea evitar es agregar la columna adicional en todas partes donde se leen los datos .

El código en sus sub-aplicaciones que necesita usar el mapeo puede ser felizmente ignorante del USER_TYPEsimple al dar a cada sub-aplicación una vista que filtre los mapeos hasta el tipo de usuario específico de la aplicación.

Joel Brown
fuente
3

Por experiencia, mi recomendación es elegir la coherencia sobre la elegancia o la 'mejor práctica'. Es hacer coincidir el diseño existente e ir con TRES tablas de correo (una para cada rol) con una mailing_id, user_idestructura de campo simple .

Es poco elegante pero tiene algunas ventajas ...

  1. Hacer coincidir la estructura existente será más fácil para cualquier otra persona que trabaje en este esquema antes de ponerlo en pastoreo.
  2. No tiene campos desperdiciados y no le está pidiendo a la base de datos que combine cosas que no existirán.
  3. Debido a que cada tabla solo estará entre sí y será relativamente fácil hacer una vista que vincule todos los datos para que las usen sus rutinas.

Estoy seguro de que muchos otros no estarán de acuerdo con este enfoque, pero los objetivos principales de la normalización y las mejores prácticas son hacer que el código sea más coherente para que sea más fácil de seguir y depurar ... y, obviamente, no es factible poner a cero toda la base de código.

James Snell
fuente
El problema con ese enfoque es que la base de datos no puede forzar la unicidad en los identificadores de correo, que es el objetivo principal de la asignación en primer lugar: de lo contrario, podría emparejarse los campos de ID individuales de cada tabla, con un indicador de "tipo de usuario". hecho sin ningún cambio.
GeminiDomino
Veo a qué te refieres, pero después de haber trabajado en ese tipo de sistema, te he dado una opción que quizás no hayas considerado. Por lo que veo, la identificación de correo necesitaría algún contenido para referirse a alguna parte (qué se envió o cómo encontrar el documento), por lo que la identificación de correo debería ser una clave externa de todos modos, lo que significa que los problemas de singularidad se resolverían en otro lugar. A medida que lo leo, las tablas de datos de los estudiantes de administración y los profesionales que están vinculadas pueden tener diferentes estructuras, por lo que no puedo ver el valor del campo de tipo de usuario. Los desarrolladores originales deben haber tocado este problema, ¿qué hicieron?
James Snell el
El campo "tipo de usuario" determinaría qué tabla asociar con ese registro en particular. Tendría que manejarse a nivel de aplicación de cualquier manera, y dado que ESTÁN en tablas diferentes, no hay una buena manera de convertirlo en una restricción de clave externa. Los desarrolladores originales no parecen haber considerado este problema en absoluto, desafortunadamente, por eso se está convirtiendo en un desastre. :)
GeminiDomino