Diseño de base de datos por primera vez: ¿estoy sobreingeniería? [cerrado]


246

Antecedentes

Soy estudiante de CS de primer año y trabajo a tiempo parcial para la pequeña empresa de mi padre. No tengo experiencia en el desarrollo de aplicaciones del mundo real. He escrito guiones en Python, algunos cursos en C, pero nada como esto.

Mi papá tiene un pequeño negocio de capacitación y actualmente todas las clases se programan, graban y siguen a través de una aplicación web externa. Hay una función de exportación / "informes" pero es muy genérica y necesitamos informes específicos. No tenemos acceso a la base de datos real para ejecutar las consultas. Me han pedido que configure un sistema de informes personalizado.

Mi idea es crear las exportaciones genéricas de CSV e importarlas (probablemente con Python) en una base de datos MySQL alojada en la oficina todas las noches, desde donde puedo ejecutar las consultas específicas que se necesitan. No tengo experiencia en bases de datos, pero entiendo los conceptos básicos. He leído un poco sobre la creación de bases de datos y los formularios normales.

Podemos comenzar a tener clientes internacionales pronto, así que quiero que la base de datos no explote si eso sucede. Actualmente también tenemos un par de grandes corporaciones como clientes, con diferentes divisiones (por ejemplo, empresa matriz de ACME, división de atención médica de ACME, división de cuidado corporal de ACME)

El esquema que se me ocurrió es el siguiente:

  1. Desde la perspectiva del cliente:
    • Clientes es la mesa principal
    • Los clientes están vinculados al departamento para el que trabajan
      • Los departamentos se pueden dispersar por un país: RRHH en Londres, Marketing en Swansea, etc.
      • Los departamentos están vinculados a la división de una empresa.
    • Las divisiones están vinculadas a la empresa matriz.
  2. Desde la perspectiva de las clases:
    • Sesiones es la mesa principal
      • Un profesor está vinculado a cada sesión.
      • Se proporciona un statusid a cada sesión. Por ejemplo, 0: completado, 1: cancelado
      • Las sesiones se agrupan en "paquetes" de un tamaño arbitrario
    • Cada paquete se asigna a un cliente

"Diseñé" (más bien garabateó) el esquema en una hoja de papel, tratando de mantenerlo normalizado a la 3ra forma. Luego lo conecté a MySQL Workbench y lo hizo todo bonito para mí:
( Haga clic aquí para ver el gráfico a tamaño completo )

texto alternativo
(fuente: maian.org )

Consultas de ejemplo que estaré ejecutando

  • Qué clientes con crédito aún quedan están inactivos (aquellos sin una clase programada en el futuro)
  • ¿Cuál es la tasa de asistencia por cliente / departamento / división (medida por la identificación de estado en cada sesión)
  • ¿Cuántas clases ha tenido un maestro en un mes?
  • Marcar clientes que tienen baja tasa de asistencia
  • Informes personalizados para departamentos de recursos humanos con tasas de asistencia de personas en su división

Pregunta (s)

  • ¿Esto es de ingeniería excesiva o me dirijo en la dirección correcta?
  • ¿La necesidad de unir varias tablas para la mayoría de las consultas dará como resultado un gran éxito en el rendimiento?
  • He agregado una columna 'última sesión' a los clientes, ya que probablemente será una consulta común. ¿Es una buena idea o debería mantener la base de datos estrictamente normalizada?

Gracias por tu tiempo


131
Estimado estudiante de primer año de CS: siga usando StackOverflow. Su pregunta es interesante, bien escrita y útil. En otras palabras, usted está en el 1% superior de los que hacen preguntas.
Adam Crossland

¿Puede una división contener otras divisiones? SI ese es el caso, una tabla "tiene" podría usarse para vincular la División con la División en la que está contenida.
Mark Schultheiss

Gracias por los amables comentarios :) Mark Tendré que revisar la documentación de este proyecto nuevamente, pero no creo que hayamos identificado ese caso. Gracias por mencionarlo.
bob esponja

1
No me gustan tus convenciones de nombres de claves principales. La tabla divisionstiene una columna llamada divisionid. ¿No te parece redundante? Solo nómbralo id. también sus nombres de tabla, incluidos _has_: eliminaría eso y solo lo nombraría, por ejemplo cities_departments. sus DATETIMEcolumnas deben ser de tipo a TIMESTAMPmenos que sean valores ingresados ​​por el usuario. Creo que es una buena idea tener las tablas citiesy countries. puede tener problemas para limitar las tablas a una sola status. considere usar un INTy realice comparaciones bit a bit en él, para que pueda tener más significado allí
James

@binnyb Hay muchos argumentos sobre el uso de id como el nombre de la clave principal que las personas deben considerar antes de decidir.
Jedi

Respuestas:


42

Algunas respuestas más a sus preguntas:

1) Estás bastante en el blanco para alguien que se acerca a un problema como este por primera vez. Creo que los consejos de otros sobre esta cuestión hasta ahora prácticamente lo cubren. ¡Buen trabajo!

2 y 3) El éxito en el rendimiento que tendrá dependerá en gran medida de tener y optimizar los índices correctos para sus consultas / procedimientos particulares y, lo que es más importante, el volumen de registros. A menos que esté hablando de más de un millón de registros en sus tablas principales, parece estar en camino de tener un diseño lo suficientemente convencional como para que el rendimiento no sea un problema en un hardware razonable.

Dicho esto, y esto se relaciona con su pregunta 3, con el comienzo que tiene probablemente no debería preocuparse demasiado por el rendimiento o la hipersensibilidad a la ortodoxia de normalización aquí. Este es un servidor de informes que está creando, no un servidor de aplicaciones basado en transacciones, que tendría un perfil muy diferente con respecto a la importancia del rendimiento o la normalización. Una base de datos que respalda una aplicación de registro y programación en vivo debe tener en cuenta las consultas que tardan segundos en devolver los datos. Una función del servidor de informes no solo tiene más tolerancia para consultas complejas y largas, sino que las estrategias para mejorar el rendimiento son muy diferentes.

Por ejemplo, en un entorno de aplicación basado en transacciones, sus opciones de mejora del rendimiento pueden incluir la refactorización de los procedimientos almacenados y las estructuras de la tabla en el enésimo grado, o el desarrollo de una estrategia de almacenamiento en caché para pequeñas cantidades de datos comúnmente solicitados. En un entorno de informes, ciertamente puede hacer esto, pero puede tener un impacto aún mayor en el rendimiento al introducir un mecanismo de instantánea donde se ejecuta un proceso programado y almacena informes preconfigurados y sus usuarios acceden a los datos de la instantánea sin estrés en su nivel de base de datos en a por solicitud.

Todo esto es una queja larga para ilustrar que los principios de diseño y los trucos que empleas pueden diferir dado el papel de la base de datos que estás creando. Espero que sea útil.


1
1. ¡Gracias, eso es tranquilizador! 2 y 3. Todavía no sé cómo funcionan los índices, es algo sobre lo que he planeado leer. Si alguna vez tenemos el "problema" de alcanzar un millón de registros, probablemente habrá un presupuesto para contratar desarrolladores experimentados: P Gracias por la comprensión de los diferentes roles de db que existen, todo es nuevo para mí y muy interesante de saber. Analizaré las instantáneas ya que lo que usted describe es básicamente el objetivo final del proyecto.
bob esponja

Si comprende las tablas, los fundamentos de los índices son bastante fáciles. Conceptualmente, un índice puede implementarse (y con frecuencia se implementa) como una tabla con muy pocas columnas cuyo contenido se copia de la tabla principal, y una referencia a la tabla principal, cuyas filas se ordenan por keot para una accesibilidad rápida. B + Tree es la disposición de índice más común, pero las optimizaciones de índice son donde los grandes jugadores tienen sus tecnologías diferenciadoras, por lo que se vuelve turbio si intentas aplicar la analogía demasiado profundamente.
pojo-guy

14

Tienes la idea correcta. Sin embargo, puede limpiarlo y eliminar algunas de las tablas de mapeo (tiene *).

Lo que puede hacer es en la tabla Departamentos, agregar CityId y DivisionId.

Además de eso, creo que todo está bien ...


44
Creo que necesita las tablas de mapeo si quiere reutilizar una definición de departamento en diferentes divisiones o ciudades.
Jacob G

1
Sí, estoy de acuerdo ... pero parecía que un departamento solo podía estar en una ciudad / división. Si no, entonces lo que tenía era definitivamente correcto.
Reverendo Gonzo

Tengo un artículo wiki que escribí con una "especificación" en la oficina, tendré que volver a leerlo, pero Jacob G tiene razón, IIRC hay algunos departamentos que abarcan divisiones. Un departamento de recursos humanos de los padres de ACME para la atención médica de ACME y el cuidado corporal de ACME. Si puedo simplificarlo, sin duda lo haré, gracias por la sugerencia.
bob esponja

6

Los únicos cambios que haría son:
1- Cambie su VARCHAR a NVARCHAR, si se va a internacionalizar, puede que desee unicode.

2- Cambie sus ID de int a GUID (identificador único) si es posible (esta podría ser mi preferencia personal). Suponiendo que finalmente llegue al punto donde tiene múltiples entornos (dev / test / staging / prod), es posible que desee migrar datos de uno a otro. Tener ID de GUID hace esto significativamente más fácil.

3- Tres capas para su empresa -> División -> La estructura del departamento puede no ser suficiente. Ahora, esto podría ser una ingeniería excesiva, pero podría generalizar esa jerarquía de modo que pueda soportar n niveles de profundidad. Esto hará que algunas de sus consultas sean más complejas, por lo que puede que no valga la pena el intercambio. Además, podría ser que cualquier cliente que tenga más capas pueda "integrarse" fácilmente en este modelo.

4- También tiene un estado en la tabla de clientes que es un VARCHAR y no tiene ningún enlace a la tabla de estados. Esperaría un poco más de claridad sobre lo que representa el estado del cliente.


1- Gracias, he estado teniendo problemas con diacríticos y UTF8 para los cuales iba a publicar otra pregunta. Quizás este sea el problema. 2- He leído algunas otras preguntas aquí sobre SO con muchas opiniones contradictorias sobre el tema, leeré más sobre el tema. 3- Volveré a hablar de esto con mi padre, mirando la "especificación" que escribí y veré si es algo que deberíamos analizar. --Contenido siguiente comentario
bob esponja

4- No profundicé en la pregunta principal por brevedad: el estado del cliente es si están activos (tienen sesiones restantes) o inactivos (no quedan sesiones). Por más claridad, ¿te refieres a un nombre más descriptivo para la columna? Por ejemplo, inscripción_estado? Gracias por tu contribución.
bob esponja

re # 4- Además de su nombre más claro, si solo hay dos estados, activo / inactivo, ¿por qué no simplemente convertirlo en una columna de bits?
Jacob G

3
No estoy de acuerdo con los GUID, estremecimiento. Pueden ser horribles para el rendimiento. No los use a menos que necesite responder.
HLGEM

1
El rendimiento solo entra en juego cuando estás hablando de 10 de millones de filas en una tabla. Si tiene ese tipo de estructura, puede mitigarlo con guías secuenciales e indexación creativa. De lo contrario, el "rendimiento" es una pista falsa cuando se descuentan los GUID.
Jacob G

6

No. Parece que estás diseñando con un buen nivel de detalle.

Creo que los países y las empresas son realmente la misma entidad en su diseño, como lo son las ciudades y las divisiones. Me desharía de las tablas de Países y Ciudades (y Cities_Has_Departments) y, si es necesario, agrego un indicador booleano IsPublicSector a la tabla Companies (o una columna CompanyType si hay más opciones que simplemente Sector privado / Sector público).

Además, creo que hay un error en su uso de la tabla Departamentos. Parece que la tabla Departamentos sirve como referencia para los diversos tipos de departamentos que puede tener cada división de clientes. Si es así, debería llamarse DepartmentTypes. Pero sus clientes (que, supongo, son asistentes) no pertenecen a un tipo de departamento, pertenecen a una instancia de departamento real en una empresa. Tal como está ahora, sabrá que un cliente determinado pertenece a un departamento de recursos humanos en algún lugar, ¡pero no a cuál!

En otras palabras, los Clientes deben estar vinculados a la tabla que usted llama Divisions_Has_Departments (pero que yo llamaría simplemente Departamentos). Si esto es así, debe colapsar las Ciudades en Divisiones como se discutió anteriormente si desea utilizar la integridad referencial estándar en la base de datos.


La tabla de países es para si / cuando tenemos clientes que operan en más de un país y tienen un departamento de Recursos Humanos diferente para cada uno. De esa manera podemos crear informes con datos del país en el que opera el departamento con el que estamos trabajando. Lo mismo para los departamentos y ciudades, creo que tenemos un cliente que tiene departamentos de Recursos Humanos separados. para las dos ciudades en las que tienen oficinas principales. O al menos ese fue el razonamiento, me sentaré y lo repensaré para ver si realmente son necesarias. No había pensado en CompanyType, averiguaré si eso es algo que necesitamos rastrear.
bob esponja

RE: depts table, mi pista de pensamiento original era usarla como departamentos reales, con el nombre del departamento como tipo. No se me había ocurrido tener solo tipos de departamento, lo que parece más lógico. Sobre saber a qué departamento y dónde pertenece alguien, pensé que tener el departamento vinculado a una ciudad y división (que está vinculada a una empresa) habría funcionado. ¿Estaba equivocado? Para colapsar ciudades en divisiones, algunas divisiones abarcan varias ciudades, y creo que tal vez incluso países. Lo investigaré de nuevo. Gracias por tu contribución.
bob esponja

5

Por cierto, vale la pena señalar que si ya está generando CSV y desea cargarlos en una base de datos mySQL, LOAD DATA LOCAL INFILE es su mejor amigo: http://dev.mysql.com/doc/refman/5.1/ es / load-data.html . También vale la pena analizar Mysqlimport, y es una herramienta de línea de comandos que básicamente es un buen contenedor para el archivo de datos de carga.


3

La mayoría de las cosas ya se han dicho, pero creo que puedo agregar una cosa: es bastante común que los desarrolladores más jóvenes se preocupen demasiado por el rendimiento por adelantado, y su pregunta sobre unir tablas parece ir en esa dirección. Este es un antipatrón de desarrollo de software llamado ' Optimización prematura '. Intenta desterrar ese reflejo de tu mente :)

Una cosa más: ¿Crees que realmente necesitas las tablas de 'ciudades' y 'países'? ¿No sería suficiente tener una columna 'ciudad' y 'país' en la tabla de departamentos para sus casos de uso? Por ejemplo, ¿su aplicación necesita enumerar departamentos por ciudad y ciudades por país?


1
Por más que lo intente, sigue calculando grandes O de helloworld.c, optimiza Las tablas de ciudades y países simplemente se generaron cuando seguía los pasos para obtener una base de datos 3NF. Supongo que la ventaja que ofrecen es la coherencia para los nombres de ciudades / países. Por ejemplo, si tenemos un cliente en Munich y, por alguna razón, quien ingresa a un nuevo estudiante en el sistema de programación decide llamarlo München en lugar de Munich como para los estudiantes anteriores. También es posible que necesitemos enumerar departamentos por ciudad, tendré que verificarlo. Gracias.
bob esponja

2
¡La optimización en la fase de diseño de una base de datos es crítica! No es una optimización prematura ya que las bases de datos son significativamente más difíciles de volver a crear cuando tienen millones de registros.
HLGEM

1
No dije que no debería poner a prueba su diseño :)
Hans Westerbeek

3

Los siguientes comentarios se basan en el rol de especialista en Business Intelligence / Reporting y gerente de estrategia / planificación:

  1. Estoy de acuerdo con la dirección de Larry arriba. En mi humilde opinión, no es demasiado sobre ingeniería, algunas cosas simplemente parecen un poco fuera de lugar. Para simplificar, etiquetaría al cliente directamente a una ID de empresa, Descripción de departamento, Descripción de división, ID de tipo de departamento, ID de tipo de división. Utilice el ID de tipo de departamento y el ID de tipo de división como referencias a las tablas de búsqueda y a los campos internos de informes / análisis para obtener coherencia a largo plazo.

  2. La tabla de paquetes contiene la columna "Crédito", ¿no debería estar realmente vinculado a la tabla base del Cliente, por lo que si tienen muchos paquetes, puede ver cuánto crédito le queda para las clases futuras? La aplicación puede encargarse del cálculo y almacenarlo centralmente en la tabla Cliente.

  3. La información de la compañía podría usar muchos más campos, incluida la dirección / teléfono / etc obvio. información. También estaría preparado para agregar columnas DUN "DUN" (Sitio / Sucursal / Ultimate) a largo plazo, Dun and Bradstreet (D&B) tiene un gran catálogo de compañías y más adelante encontrará que su información es muy útil para informes / análisis. Esto se encargará del problema de división múltiple que mencione y le permitirá acumular su jerarquía para sub / division / sucursales / etc. de grandes cuerpos.

  4. No mencionas con cuántos registros trabajarás, lo que podría implicar prepararte para una gran iniciativa de desarrollo que podría haberse hecho más rápido y con muchos menos dolores de cabeza con el software de "informes" preempaquetado. Si no está lidiando con una gran base de datos (<65000) filas, asegúrese de que MS-Access, OpenOffice (Base) o las soluciones de desarrollo de informes / aplicaciones relacionadas no podrían hacer el truco. Yo uso bastante el software APEX gratuito de Oracle, viene con su base de datos gratuita Oracle XE, solo descárguelo de su sitio.

  5. FYI - Información de informes: para grandes bases de datos, normalmente tiene dos instancias de base de datos a) base de datos de transacciones para registrar cada registro detallado. b) base de datos de informes (data mart / data warehouse) alojada en una máquina separada. Para obtener más información, busque en Google tanto Star Schema como Snowflake Schema.

Saludos.


1. ¿Quiere decir agregar todas esas columnas a la tabla del cliente? Creo que eso rompería la normalización y también dificultaría mantener la coherencia, aunque no estoy seguro de haberlo entendido correctamente. 2. Los paquetes son secuenciales, solo el paquete más reciente puede tener crédito pendiente, por lo que no es necesario realizar un seguimiento de varios paquetes. ¿Seguiría recomendando almacenarlo en la tabla del cliente en este caso? 3. Esto parece ser muy útil para descubrir la estructura de las empresas clientes, lo investigaré gracias.
bob esponja

4. Tendré que verificar la cantidad de clientes y sesiones que esperamos tener durante el próximo año, pero me parece factible que la tabla de sesiones alcance esa cantidad de filas en un año más o menos. Investigaré el software de informes, no se me había ocurrido. 5. Parece que esa es la situación a la que he llegado por accidente; la aplicación web será nuestra "base de datos de transacciones" y este proyecto nuestra "base de datos de repoting" :) Gracias por su aporte.
bob esponja

1. Sí, agregando las columnas "ID de empresa, Descripción de departamento, Descripción de división, ID de tipo de departamento, ID de tipo de división" a la tabla del cliente. El cliente pertenece a una empresa, un tipo de departamento distinto (IT / Ops / Admin / etc.) Dentro de una empresa y un tipo de división distinto (líneas de negocio de Ventas / Recursos Humanos / Marketing). 2. Simplemente creo que el crédito está asociado con un cliente o empresa y no con el paquete de sesiones. Esta es una decisión comercial que puede tomar.
Será

Larry también mencionó combinar Compañía y País. Estoy totalmente de acuerdo y vuelvo al punto con respecto a la referencia de D&B. Usaría un SiteID o algo único para permitir múltiples ubicaciones de la misma compañía y luego vincularía los Departamentos a uno de los SiteID únicos.
Será

2

Quiero abordar solo la preocupación de que unirse a varias tablas provocará un éxito en el rendimiento. No tengas miedo de normalizar porque tendrás que hacer uniones. Las uniones son normales y esperadas en bases de datos relacionales y están diseñadas para manejarlas bien. Deberá establecer relaciones PK / FK (para la integridad de los datos, es importante tenerlo en cuenta en el diseño), pero en muchas bases de datos los FK no se indexan automáticamente. Como se utilizarán en las combinaciones, definitivamente querrá comenzar indexando el FKS. Las PK generalmente obtienen un índice de creación, ya que tienen que ser únicas. Es cierto que el diseño de datawarehouse reduce el número de uniones, pero generalmente no se llega al punto de almacenamiento de datos hasta que se necesita acceder a millones de registros en un informe. Incluso entonces, casi todos los almacenes de datos comienzan con una base de datos transaccional para recopilar los datos en tiempo real y luego los datos se mueven al almacén en un horario (nocturno o mensual o lo que sea necesario para el negocio). Por lo tanto, este es un buen comienzo, incluso si necesita diseñar un almacén de datos más adelante para mejorar el rendimiento del informe.

Debo decir que su diseño es impresionante para un estudiante de CS de primer año.


1

No está sobre diseñado, así es como abordaría el problema. Unirse está bien, no habrá mucho impacto en el rendimiento (¡es completamente necesario a menos que desnormalice la base de datos, lo que no se recomienda!). Para los estados, vea si puede usar un tipo de datos enum para optimizar esa tabla.


las enumeraciones son malas. Cada vez que necesite extender la enumeración, debe reconstruir su tabla, lo cual está bien hasta que su tabla tenga muchos GB.
Martin

Gracias por el aporte y la sugerencia Chris, me preocupaba estar creando un monstruo demasiado complejo. Martin, los estados están bastante bien definidos y estáticos: básicamente 0-Clase completa, 1-Clase cancelada, 2-No apareció. Creo que estos tres cubren cualquier posible resultado de una clase. ¿Sigue siendo una mala idea usar enumeraciones en este caso?
bob esponja

Esto parece perfecto para una enumeración, en mi mente. Todos los resultados posibles se satisfacen con anticipación. También está bien un int que puede representar mediante una enumeración o entradas estáticas en su aplicación. Realmente no importa :) Las enumeraciones son más agradables de ver si edita su base de datos utilizando alguna herramienta.
Chris Dennett

las enumeraciones pueden ser problemáticas (quizás mal es una palabra demasiado fuerte) cuando tiene tablas grandes que deben estar en línea 24x7 y la enumeración debe cambiarse. Dado que está repoblando las tablas desde cero, no se preocupe por eso. Dado un conjunto de datos lo suficientemente pequeño, también podría usar cadenas.
Martin

1

He trabajado en el ámbito de la formación / escuela y pensé en señalar que generalmente hay una relación M: 1 entre lo que llaman "sesiones" (instancias de un curso determinado) y el curso en sí. En otras palabras, su catálogo ofrece el curso ("Español 101" o lo que sea), pero puede tener dos instancias diferentes durante un solo semestre (Tu-Th enseñado por Smith, Wed-Fri enseñado por Jones).

Aparte de eso, parece un buen comienzo. Apuesto a que encontrará que el dominio del cliente (gráficos que conducen a "clientes") es más complejo de lo que ha modelado, pero no se exceda con eso hasta que tenga algunos datos reales que lo guíen.


Si te he entendido bien, no es el caso. Los "cursos" son solo grupos de sesiones posteriores. No es un sistema tradicional basado en un semestre. No se me ocurre nada más que se pueda agregar al dominio del cliente, ¿tiene algún ejemplo? También me preocupaba haberme excedido por la complejidad, me alegro de que no sea así :) Gracias por su aporte.
bob esponja

0

Algunas cosas me vinieron a la mente:

  1. Las mesas parecían orientadas a la presentación de informes, pero en realidad no dirigían el negocio. Creo que cuando un cliente se registra, esencialmente se hace un pedido para el cliente que asiste a una lista de sesiones, y ese pedido podría ser para varios empleados en una empresa. Parecería que una tabla de "pedidos" realmente estaría en el centro de su sistema e impulsaría la captura de datos y los informes eventuales. (Compare los documentos en papel que ha estado utilizando para administrar el negocio con el diseño de su base de datos para ver si hay una coincidencia lógica).

  2. Las empresas a menudo no tienen divisiones. Los empleados a veces cambian divisiones / departamentos, tal vez incluso a mitad de sesión. Las empresas a veces agregan / eliminan / renombran divisiones / departamentos. Asegúrese de que el posible cambio en tiempo real del contenido de sus tablas no dificulte la creación de informes / agrupaciones posteriores. Con tantos datos de contacto divididos en tantas tablas, es posible que deba aplicar una validación de entrada de datos muy estricta para mantener sus informes significativos e inclusivos. Por ejemplo, cuando se agrega un nuevo cliente, asegurarse de que su empresa / división / departamento / ciudad coincida con los mismos valores que sus compañeros de trabajo.

  3. El concepto de "paquetes" no está claro en absoluto.

  4. Como indica que es una pequeña empresa, sería sorprendente que el rendimiento fuera un problema, teniendo en cuenta la velocidad y la capacidad de las máquinas actuales.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.