¿Por qué es importante el modelo relacional para una base de datos?

61

Me estoy acercando a un proyecto en el que tendré que implementar una base de datos con mi jefe; Somos un inicio muy pequeño, por lo que el entorno de trabajo es profundamente personal.

Él me había dado una de las bases de datos de la compañía antes y estaba completamente en contra de lo que me enseñaron (y leyeron) en la escuela para RDBMS. Por ejemplo, aquí hay bases de datos completas que constan de una tabla (por base de datos independiente). Una de esas tablas tiene más de 20 columnas y, para el contexto, aquí están algunos de los nombres de columna de la tabla one :

  

lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | ID de producto lng | vrProductName

El punto es que, donde debe tener tablas individuales que contienen los datos de la entidad (nombre, tamaño, fecha de compra, etc.), los guarda todos en una tabla grande por base de datos.

Quiero mejorar este diseño, pero no estoy seguro de por qué un modelo de datos correctamente normalizado y segmentado mejoraría este producto. Aunque estoy familiarizado con el diseño de bases de datos de la universidad y entiendo cómo hacerlo, no estoy seguro de por qué esto realmente mejora las bases de datos.

¿Por qué un buen esquema relacional mejora una base de datos?

    
pregunta 8protons 26.04.2016 - 17:02

7 respuestas

71

El argumento de rendimiento suele ser el más intuitivo. Especialmente desea señalar cómo será difícil agregar buenos índices en una base de datos incorrectamente normalizada (nota: hay casos extremos en los que la desnormalización puede, de hecho, mejorar el rendimiento, pero cuando ambos no tienen experiencia bases de datos relacionales es probable que no veas fácilmente estos casos).

Otro es el argumento del tamaño de almacenamiento. Una tabla desnormalizada con muchas redundancias requerirá mucho más almacenamiento. Esto también influye en el aspecto del rendimiento: cuantos más datos tenga, más lentas serán sus consultas.

También hay un argumento que es un poco más difícil de entender, pero de hecho es más importante porque no puedes resolverlo lanzándole más hardware. Ese es el problema de la consistencia de los datos. Una base de datos correctamente normalizada se encargará de que un producto con una ID específica siempre tenga el mismo nombre. Pero en una base de datos desnormalizada, tales inconsistencias son posibles, por lo que se debe tener especial cuidado cuando se trata de evitar inconsistencias, lo que llevará el tiempo de programación correcto y aún causará errores que le costarán la satisfacción del cliente.

    
respondido por el Philipp 26.04.2016 - 17:10
24
  

Tendré que implementar una base de datos con mi jefe ...

Usar el software dedicado de gestión de bases de datos podría ser considerablemente más fácil (lo siento; no pude resistirme).

  

lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | ID de producto lng | vrProductName

Si a esta base de datos solo le importa "registrar" qué producto se vendió dónde, cuándo y quién lo hizo, entonces usted podría ser capaz de ampliar la definición de "OK base de datos" lo suficiente para cubrirla. Si esta información se está utilizando para cualquier cosa más, entonces es realmente bastante pobre.

Pero ...

¿Las aplicaciones / consultas que utilizan estos datos responden de manera deficiente / lenta? Si no, entonces no hay un problema real para resolver. Claro, se ve y se siente feo, pero si funciona , entonces no obtendrás "puntos" por sugerir que "podría" ser mejor.

Si puede encontrar síntomas definidos (es decir, problemas) que parecen haber sido causados por un mal modelado de datos, prototipo de una mejor solución. Tome una copia de una de estas "bases de datos", normalice los datos y vea si su solución funciona mejor. Si es considerablemente mejor (y espero que todas las operaciones de actualización de cualquier en estos datos se mejoren masivamente ), entonces vuelva a su jefe y Muéstrales la mejora.

Es perfectamente posible recrear su "vista de una sola tabla" de los datos con ... bueno ... Vistas.

    
respondido por el Phill W. 26.04.2016 - 17:26
14
  

¿Por qué un buen esquema relacional mejora una base de datos?

La respuesta es: no siempre mejora una base de datos. Debe saber que lo que probablemente le enseñaron se llama Tercera forma normal .

Otras formas son válidas en algunas situaciones, lo cual es clave para responder a su pregunta. Su ejemplo se parece a First Form Normal , si eso le ayuda a sentirse mejor acerca de su estado actual.

Las reglas 3NF establecen relaciones entre los datos que "mejoran" una base de datos:

  1. Evita que datos no válidos ingresen a tu sistema (si una relación es 1-a-1, genera un error a pesar del código escrito encima). Si sus datos son consistentes en la base de datos, es menos probable que resulte en inconsistencias fuera de su base de datos.

  2. Proporciona una forma de validar el código (por ejemplo, una relación de muchos a uno es una señal para restringir las propiedades / comportamientos de un objeto). Al escribir código para usar la base de datos, a veces los programadores notan la estructura de datos como un indicador de cómo debería funcionar su código. O pueden proporcionar comentarios útiles si la base de datos no coincide con su código. (Desafortunadamente, esto es más como una ilusión).

  3. Proporcione reglas que pueden ayudarlo significativamente a reducir los errores al crear una base de datos, de modo que no la genere basándose en requisitos arbitrarios que pueden surgir en cualquier momento durante la vida de una base de datos. En su lugar, está evaluando sistemáticamente la información para lograr objetivos específicos.

  4. Las estructuras de base de datos adecuadas conducen a un mejor rendimiento al conectar los datos de manera que minimizan el almacenamiento de datos, minimizan las llamadas de almacenamiento para recuperar datos, maximizan los recursos en la memoria y / o minimizan la clasificación / manipulación de datos para el conjunto de datos en particular que tiene, en comparación con la consulta que está ejecutando contra él. Pero la estructura "adecuada" depende de la cantidad de datos, la naturaleza de los datos, el tipo de consulta, los recursos del sistema, etc. Al normalizar puede empeorar el rendimiento (es decir, si carga todos los datos como 1 tabla, la unión puede ralentizarse). una consulta). El procesamiento de transacciones (OLTP) frente a la inteligencia empresarial (almacén de datos) es muy diferente.

En una pequeña empresa con pequeños conjuntos de datos, es posible que no haya nada de malo en su forma actual. Excepto, si usted crece, será difícil "arreglarlo" más adelante, ya que a medida que la tabla crece, es probable que los sistemas que la usan vayan más lentos.

Por lo general, querrá enfatizar las transacciones rápidas a medida que la empresa crece. Sin embargo, si pasa tiempo en este proyecto ahora en lugar de otras cosas que la compañía puede necesitar con mayor urgencia, es posible que nunca tenga ese problema porque su compañía nunca crece. Ese es el "desafío de la optimización previa": dónde pasar su valioso tiempo ahora mismo.

¡Buena suerte!

    
respondido por el Jim 26.04.2016 - 22:20
11

Hay varias razones por las que usar una gran "tabla de dios" es malo. Intentaré ilustrar los problemas con una base de datos de ejemplo compuesta. Supongamos que está tratando de modelar eventos deportivos. Diremos que desea modelar juegos y los equipos que juegan en esos juegos. Un diseño con varias tablas podría tener este aspecto (esto es muy simple a propósito, así que no se deje atrapar en lugares donde se pueda aplicar más normalización):

Teams
Id | Name | HomeCity

Games
Id | StartsAt | HomeTeamId | AwayTeamId | Location

y una base de datos de tabla única se vería así

TeamsAndGames
Id | TeamName | TeamHomeCity | GameStartsAt | GameHomeTeamId | GameAwayTeamId | Location

Primero, veamos cómo hacer índices en esas tablas. Si necesitaba un índice en la ciudad local para un equipo, podría agregarlo a la tabla Teams o la tabla TeamsAndGames con bastante facilidad. Recuerde que siempre que cree un índice, debe almacenarlo en un disco y actualizarlo a medida que se agregan filas a la tabla. En el caso de la tabla Teams esto es bastante sencillo. Puse en un nuevo equipo, la base de datos actualiza el índice. ¿Pero qué hay de TeamsAndGames ? Bueno, lo mismo se aplica a partir del ejemplo Teams . Añado un equipo, el índice se actualiza. ¡Pero también sucede cuando agrego un juego! Aunque ese campo será nulo para un juego, el índice todavía debe actualizarse y almacenarse en el disco para ese juego de todos modos. Para un índice, esto no suena tan mal. Pero cuando necesita muchos índices para las múltiples entidades agrupadas en esta tabla, desperdicia mucho espacio almacenando los índices y mucho tiempo de procesador actualizándolos para cosas donde no se aplican.

Segundo, consistencia de los datos. En el caso de usar dos tablas separadas, puedo usar claves externas de la tabla Games a la tabla Teams para definir qué equipos están jugando en un juego. Y, asumiendo que las columnas HomeTeamId y AwayTeamId no sean anulables, la base de datos garantizará que cada juego que coloque tenga 2 equipos y que esos equipos existan en mi base de datos. Pero ¿qué pasa con el escenario de una sola mesa? Bueno, ya que hay varias entidades en esta tabla, esas columnas deben ser anulables (puede hacer que no sean anulables y meter datos de basura allí, pero eso es una idea horrible). Si esas columnas son anulables, la base de datos ya no puede garantizar que al insertar un juego tenga dos equipos.

Pero, ¿y si decides ir solo por ello? Configura las claves externas de modo que esos campos apunten a otra entidad en la misma tabla. Pero ahora la base de datos solo se asegurará de que esas entidades existan en la tabla, no que sean del tipo correcto. Usted podría establecer fácilmente GameHomeTeamId a la ID de otro juego y la base de datos no se quejará en absoluto. Si lo intentara en el escenario de varias tablas, la base de datos generaría un ajuste.

Puede intentar mitigar estos problemas diciendo "bueno, nos aseguraremos de que nunca lo hagamos en el código". Si confía en su capacidad para escribir código libre de errores la primera vez y en su capacidad para tener en cuenta cada combinación extraña de cosas que un usuario podría intentar, adelante. Personalmente, no confío en mi capacidad para hacer ninguna de estas cosas, así que dejaré que la base de datos me proporcione una red de seguridad adicional.

(Esto empeora aún más si su diseño es uno donde se copian todos los datos relevantes entre filas en lugar de usar claves externas. Cualquier ortografía / otras inconsistencias en los datos serán difíciles de resolver. ¿Cómo puede saber si "Jon" es un error ortográfico? de "John" o si fue intencional (porque son dos personas distintas)?

Tercero, casi todas las columnas deben ser anulables o deben rellenarse con datos copiados o de basura. Un juego no necesita un TeamName o TeamHomeCity . Entonces, o cada juego necesita algún tipo de marcador de posición allí o debe ser anulable. Y si es anulable, la base de datos tomará un juego sin TeamName . También tomará un equipo sin nombre, incluso si la lógica de su negocio dice que eso nunca debería suceder.

Hay otras razones por las que querrías tablas separadas (incluida la preservación de la cordura del desarrollador). Incluso hay algunas razones por las que una tabla más grande podría ser mejor (la desnormalización a veces mejora el rendimiento). Esos escenarios son pocos y distantes entre sí (y generalmente se manejan mejor cuando tienes métricas de rendimiento para mostrar que ese es realmente el problema, no un índice faltante o algo más).

Finalmente, desarrolle algo que sea fácil de mantener. Solo porque "funciona" no significa que esté bien. Tratar de mantener las tablas de dioses (como las clases de dioses) es una pesadilla. Solo te estás preparando para el dolor más tarde.

    
respondido por el Becuzz 26.04.2016 - 19:01
6

Cita del día: " La teoría y la práctica deben ser las mismas ... en teoría "

Tabla desnormalizada

Su única tabla de hold-it-all contiene datos redundantes tiene una ventaja: hace que los informes en sus líneas sean muy sencillos de codificar y rápidos de ejecutar porque no tiene que hacer ninguna combinación. Pero esto a un alto costo:

  • Contiene copias redundantes de relaciones (por ejemplo, IngCompanyID y vrCompanyName ). La actualización de los datos maestros puede requerir actualizar muchas más líneas que en un esquema normalizado.
  • Se mezcla todo. No puede garantizar un control de acceso fácil en el nivel de la base de datos, por ejemplo, garantizando que el usuario A pueda actualizar solo la información de la empresa y el usuario B solo la información del producto.
  • No puede garantizar las reglas de coherencia en el nivel de la base de datos (por ejemplo, la clave principal para hacer cumplir que solo hay un nombre de empresa para un ID de empresa).
  • No se beneficia completamente del optimizador de DB que podría identificar estrategias de acceso óptimas para una consulta compleja, aprovechando el tamaño de las tablas normalizadas y las estadísticas de varios índices. Esto podría compensar rápidamente el beneficio limitado de evitar uniones.

Tabla normalizada

Las desventajas anteriores son ventajas para el esquema normalizado. Por supuesto, las consultas pueden ser un poco más complejas de escribir.

En resumen, el esquema normalizado expresa mucho mejor la estructura y las relaciones entre sus datos. Seré provocativo y diré que es el mismo tipo de diferencia que entre la disciplina requerida para usar un juego de cajones de oficina ordenados y la facilidad de uso de un contenedor de basura.

    
respondido por el Christophe 27.04.2016 - 00:41
5

Creo que hay al menos dos partes en tu pregunta:

1. ¿Por qué no deberían almacenarse entidades de diferentes tipos en la misma tabla?

Las respuestas más importantes aquí son la legibilidad y la velocidad del código. Un SELECT name FROM companies WHERE id = ? es mucho más legible que un SELECT companyName FROM masterTable WHERE companyId = ? y es menos probable que consulte sin sentido las tonterías (por ejemplo, SELECT companyName FROM masterTable WHERE employeeId = ? no sería posible cuando las empresas y los empleados estén almacenados en diferentes tablas). En cuanto a la velocidad, los datos de una tabla de base de datos se recuperan leyendo la tabla completa de forma secuencial o leyendo un índice. Ambos son más rápidos si la tabla / índice contiene menos datos, y ese es el caso si los datos se almacenan en tablas diferentes (y solo necesita leer una de las tablas / índices).

2. ¿Por qué las entidades de un solo tipo se dividen en subentidades que se almacenan en diferentes tablas?

Aquí, el motivo es principalmente para evitar inconsistencias en los datos. Con el enfoque de tabla única, para un sistema de gestión de pedidos, puede almacenar el nombre del cliente, la dirección del cliente y la identificación del producto del producto que el cliente solicitó como una sola entidad. Si un cliente ordenara varios productos, tendría varias instancias del nombre y la dirección del cliente en su base de datos. En el mejor de los casos, acaba de obtener datos duplicados en su base de datos, lo que puede ralentizarlo un poco. Pero un caso peor es que alguien (o algún código) cometió un error cuando se ingresaron los datos, de modo que las empresas terminen con diferentes direcciones en su base de datos. Esto solo es lo suficientemente malo. Pero si tuviera que consultar la dirección de una empresa en función de su nombre (por ejemplo, SELECT companyAddress FROM orders WHERE companyName = ? LIMIT 1 ), obtendría una de las dos direcciones de manera arbitraria y ni siquiera se daría cuenta de que había una inconsistencia. Pero cada vez que ejecuta la consulta, puede obtener una dirección diferente, dependiendo de cómo el DBMS resuelva su consulta internamente. Esto probablemente romperá su aplicación en otro lugar, y la causa principal de esa rotura será muy difícil de encontrar.

Con el enfoque de tablas múltiples, se daría cuenta de que existe una dependencia funcional desde el nombre de la empresa hasta la dirección de la empresa (si una empresa puede tener solo una dirección), almacenará la ( companyName, companyAddress) tupla en una tabla (por ejemplo, company ), y la tupla (productId, companyName) en otra tabla (por ejemplo, order ). Una restricción UNIQUE en la tabla company podría imponer que cada compañía solo tenga una única dirección en su base de datos, de modo que nunca pueda surgir una inconsistencia en las direcciones de la compañía.

Nota: en la práctica, por razones de rendimiento, es probable que genere un ID de empresa único para cada compañía y lo use como una clave externa en lugar de usar el Nombre de la empresa directamente. Pero el enfoque general sigue siendo el mismo.

    
respondido por el Dreamer 27.04.2016 - 10:06
3

TL; DR : están diseñando la base de datos según cómo se les enseñó a ellos cuando estaban en la escuela.

Podría haber escrito esta pregunta hace 10 años. Me tomó algo de tiempo entender por qué mis predecesores diseñaron sus bases de datos de la misma manera que lo hicieron. Estás trabajando con alguien que:

  1. Obtuvo la mayoría de sus habilidades de diseño de base de datos utilizando Excel como base de datos o
  2. Están usando las mejores prácticas cuando salieron de la escuela.

No sospecho que sea el número 1 ya que en realidad tienes números de identificación en tu tabla, así que asumiré el número 2.

Después de salir de la escuela, estaba trabajando para una tienda que usaba un AS / 400 (también conocido como IBM i ). Encontré algunas cosas extrañas en la forma en que diseñaron sus bases de datos, y comencé a recomendar que realicemos cambios para seguir cómo me enseñaron a diseñar bases de datos. (era tonto en aquel entonces)

A un programador mayor de pacientes le costó explicarme por qué las cosas se hacían de esa manera. No habían cambiado el esquema porque habría hecho que los programas que eran más antiguos que yo se rompieran. Literalmente, el código fuente de un programa tenía una fecha de creación del año anterior a mi nacimiento. En el sistema en el que estábamos trabajando, sus programas tuvieron que implementar toda la lógica y las operaciones que el planificador de consultas de su base de datos maneja por usted. . (Puede verlo ejecutando EXPLAIN en una de sus consultas)

Estaba al tanto de las técnicas que intentaba implementar, pero mantener el sistema en funcionamiento era más importante que hacer cambios "porque iba en contra de lo que me enseñaron". Cada nuevo proyecto, cualquiera de nosotros, comenzó a hacer el mejor uso del modelo relacional que pudimos. Desafortunadamente, otros programadores / consultores de esa época aún diseñaban sus bases de datos como si estuvieran trabajando con las restricciones anteriores de ese sistema.

Algunos ejemplos de lo que encontré que no encajaba con el modelo relacional:

  • Las fechas se almacenaron como Números de día juliano que requerían una inscripción en una tabla de fechas para obtener la fecha real.
  • Tablas desnormalizadas con columnas secuenciales del mismo tipo (por ejemplo, code1,code2, ..., code20 )
  • columnas CHAR de longitud NxM que representan una matriz de N cadenas de longitud M.

Las razones por las que me dieron estas decisiones de diseño se basaron en las limitaciones del sistema cuando se diseñó la base de datos por primera vez.

Fechas : me dijeron que tomaba más tiempo de procesamiento para usar las funciones de fecha (el mes o el día de la semana) para procesar una fecha que para crear una tabla de cada fecha posible con todos esa información.

Columnas secuenciales del mismo tipo : el entorno de programación en el que estaban permitía que un programa creara una variable de matriz en una parte de la fila. Y fue una forma más fácil de reducir el número de operaciones de lectura.

Columnas NxM Length CHAR : fue más fácil incluir los valores de configuración en una columna para reducir las operaciones de lectura de archivos.

Un ejemplo mal concebido en C equivalente para reflejar el entorno de programación que tenían:

#define COURSE_LENGTH 4
#define NUM_COURSES 4
#define PERIOD_LENGTH 2

struct mytable {
    int id;
    char periodNames[NUM_COURSES * PERIOD_LENGTH];  // NxM CHAR Column
    char course1[COURSE_LENGTH];
    char course2[COURSE_LENGTH];
    char course3[COURSE_LENGTH];
    char course4[COURSE_LENGTH];
};

...

// Example row
struct mytable row = {.id= 1, .periodNames="HRP1P2P8", .course1="MATH", .course2="ENGL", .course3 = "SCI ", .course4 = "READ"};

char *courses; // Pointer used to access the sequential columns
courses = (char *)&row.course1;


for(int i = 0; i < NUM_COURSES; i++) {

    printf("%d: %.*s -> %.*s\n",i+1, PERIOD_LENGTH, &row.periodNames[PERIOD_LENGTH * i], COURSE_LENGTH,&courses[COURSE_LENGTH*i]);
}
  

Salidas

     

1: HR - > MATEMÁTICAS
  2: P1 - > ENGL
  3: P2 - > SCI
  4: P8 - > LEER

Según lo que se me dijo, parte de esto se consideró la mejor práctica en ese momento.

    
respondido por el Core.B 28.04.2016 - 05:14

Lea otras preguntas en las etiquetas