sql >> Base de Datos >  >> RDS >> Mysql

Tablón de anuncios - Optimización de la base de datos

Parte I

Revisado el 9 de diciembre de 10 a las 01:00 EST

Miré su DDL. Está bien. Necesitamos dar un paso atrás y organizar su base de datos primero. Eso resolverá la mitad de sus problemas (su SQL será sencillo y rápido; menos índices; no se requieren tablas temporales). Por un momento pensé, ajá, tienes tus columnas, debe ser estable, pero no hay posibilidad. Arriba hacia abajo desde cero, ok. Eche un vistazo a este Diagrama de Relación de Entidades (no sirve trabajar en el Modelo de Datos, que son Entidades, Relaciones y Atributos , hasta que acertemos con los ER), y comprobar que es correcto.

  • La forma de hacerlo es respondiendo las siguientes preguntas (las respuestas cortas están bien). Estas preguntas aclaran las entidades y reglas comerciales . La forma en que entiende las bases de datos en general y sus datos en particular es crucial. Has recorrido un largo camino por tu cuenta, por lo que podemos continuar desde allí.

  • Creo que ▶esta publicación◀ podría serle útil para comprender las etapas formales que se deben seguir; que estamos cortocircuitando aquí.

  • Lo más importante, total y completamente, olvídese de la función y cualquier requisito de codificación. Los datos deben modelarse independientemente de la aplicación, simplemente como datos. El modelado de funciones es una ciencia diferente. Primero haz uno bien; luego haz el otro bien; y los dos juntos tocan hermosas melodías. Intente atascarlos juntos; haciendo ambas tareas al mismo tiempo, y ni siquiera harán una banda de garaje suburbana.

Por brevedad, y por el bien de cualquiera que lea esto, utilizo una Sección Cerrada y una Abierta; cuando se cierra un elemento Abierto (discusión), lo haré conciso y lo moveré a la sección Cerrado. Mantén la numeración, porque a veces las cosas vuelven para atormentarnos. Es posible que desee hacer lo mismo, o incluso eliminar la discusión de su lado.

Los enlaces para las fotos bonitas están al final.

Disculpas:la edición no funciona; la subnumeración es inconsistente

Problemas cerrados

  1. users.bb_locations_csv es una relación de muchos a muchos entre usuarios y ubicaciones:
    • Cada uno de esos elementos debe ser una entrada en una columna discreta, en una fila discreta
    • Un usuario puede tener muchas ubicaciones y 1 ubicación puede tener muchos usuarios es de muchos a muchos
    • Lea ▶esta publicación◀ para una discusión sobre cómo se trata y en qué etapa se trata
    • En esta etapa lógica, esa es solo una relación n::n, como he dibujado, puede olvidarse de ella por ahora, se proporcionará, simplemente, cuando lleguemos a la etapa física.
    • Confía en mí, proporcionaré un código que no es más complejo que ...WHERE IN () para su propósito declarado.
    • Pensándolo bien, si te rompo los dedos, escribirás aún más lento, así que mejor no lo haga
    • Ok, su aplicación está basada en un navegador y la página es dinámica (mi consejo fue para las páginas estáticas que necesitan ser retocadas); continúe con las casillas de verificación.
      .
  2. users.bb_categories_csv es una relación de muchos a muchos entre usuarios y categorías
    • Ídem.
      .
  3. Confirmado:un boletín (bbs) no existe sin usuario; un usuario emite un boletín, y eso inicia todo el ciclo; luego invita a respuestas y calificaciones.

    3.1 Confirmado:en realidad solo hay un tablero de anuncios y no existe como una Cosa en la base de datos.

    3.2 Confirmado:que la organización nunca tendrá más de un tablón de anuncios, y las clasificaciones y categorizaciones son manejadas adecuadamente por la tabla/función Categoría

  4. Eliminado.

  5. Confirmado:la diferencia entre boletines y respuestas es que las respuestas dependen de un boletín para existir, no tienen un título y no están categorizadas por ubicación o categoría porque dependen del boletín mismo para existir.

  6. Eliminado.

  7. Comentarios anotados. Resuelto.

7.1. Por cada boletín enviado por otro usuario, cada usuario puede publicar más de una respuesta.

7.2. Para cada boletín enviado por un usuario, ese usuario puede publicar una o más de una respuesta.

7.3. Eliminado.

7.4. Eliminado.

.
8. Confirmado:cada usuario puede publicar como máximo una calificación en un boletín (que se puede revocar/modificar)
.
9. Confirmado:cada usuario puede publicar como máximo una calificación a una respuesta (ídem)

10.1. Dado:el nombre de usuario proviene de la organización y es el nombre único que identifica a los empleados. Por ejemplo, los correos electrónicos son [email protected] - la autenticación se realiza con ldap y esto es necesario para conectarse y recuperar otra información sobre los empleados

  • Confirmado:UserName es un identificador excelente

10.2. Confirmado:FirstName, LastName ... BirthPlace, etc. permanecen como columnas (tradicionales) para garantizar People no se duplican.
.
11. Dado:Por el momento podemos identificar nuestras oficinas por nombres casuales que generalmente se conocen dentro de la organización, ya que solo tenemos alrededor de 3 oficinas principales y muchas oficinas de campo. Entonces, los ejemplos serían Washington DC o la oficina local de Virginia. En total, creo que intentaremos mantener el total por debajo de 20. También quiero registrar la dirección exacta de cada ubicación porque eso podría usarse para identificar oficinas de forma única para los usuarios.

  • Provisto:StateCode+Town como PK; IsMainOffice como booleano.

.
12. Confirmado:Description y Name para Category son obligatorios.
.
13. Dado:los usuarios no podrán publicar en algunas categorías. Solo los usuarios con derechos suficientemente altos tendrán derecho a publicar en ciertas categorías.

  • Provisto:Permission en User, Location, Category es un método para evaluar tales derechos.

.
14. Confirmado:Location.Administrator es UserId de administrador para la Location .
.
15. Dado:Solo habrá necesidad de un me gusta o un disgusto. No creo que deba haber una posición neutral porque esto es lo mismo que simplemente no votar. Me gusta parece más relevante para las respuestas al boletín que para las publicaciones, para ser honesto. Es decir, veo su respuesta y, en lugar de escribir la mía, simplemente estaré de acuerdo con usted:el tablón de anuncios existente es algo así como un aspecto social en la organización y creo que gustar y disgustar/estar de acuerdo y no estar de acuerdo crea un nivel de controversia que fomenta la participación. . Sin embargo, el hecho de que te guste o no un boletín no siempre sea del todo apropiado.

15.1 Proporcionado:Like como booleano en BulletinRating y ResponseRating . Esto requerirá interpretación en cada acceso.
15.2. Cuando ya no es un booleano, se puede cambiar a un RatingCode e implementado como una tabla de búsqueda. Luego, los nombres se determinan mediante Joins y se elimina la interpretación. Dibujé esto en el Primer Modelo de Datos, para que pudieras ver lo que quise decir 15.3. Eliminado en el Segundo Modelo de Datos.
.
16. Confirmado:cada usuario tiene una Location de inicio (aparte de la lista de Locations que les interese).
.
17. Confirmado:Permission según (13).
.
18. Confirmado:es posible que se requieran más permisos, según el modelo de datos.

18.1. Si hace esto ahora, no tendrá que preocuparse por cuándo la organización decide evitar que una determinada Person de publicar Responses o Bulletins , o Calificarlos; y quiere que esa función se implemente ayer.

18.2. Incluso si no lo implementa, deje espacios entre los valores que implemente.
.
19 Confirmado:un Bulletin es sobre una Location .

19.1. Confirmado:No hay Bulletins sin una Location

19.2. Confirmado:No hay Bulletins sin una Location .

19.3 Confirmado:No hay Bulletins sin un User (declarativo). Pero hasta ahora no tenemos forma de restringir ese User; por lo tanto cualquier User puede insertar un Bulletin para cualquier Location (podría restringirlo en el código, por ejemplo, a Locations cada User Is Interested In .

19.4 Confirmado:No hay BulletinRatings sin un Bulletin y una calificación User .

19.5 Confirmado:No hay Responses sin un Bulletin .

19.4 Confirmado:No hay ResponseRatings sin una Response y una calificación User .

19.7. Pero, puede haber Users , Ubicaciones, and Categorías`, de forma independiente.

.
20. Si no le importa, proporcionaré convenciones de nomenclatura, etc. Deben explicarse por sí mismas, y el valor aparecerá solo cuando comience a codificar SQL. Por favor, pregunte, si algo no lo es. Para empezar, todos los nombres son singulares. El caso mixto es más fácil de leer (se supone que debe usar mayúsculas para el lenguaje SQL).

20.1. Mi experiencia es table_name en oposición a tableName son formas realmente técnicas, y a los usuarios no les gustan; El caso mixto consistente es del agrado de todos. Es una de esas cosas que es imposible cambiar, así que elige con cuidado.
.
21. Para su necesidad de agrupar mesas, lo cual es bueno, tenga en cuenta que se trata de un problema físico. En el nivel del modelo de datos lógicos, las tablas tienen nombres normales, libres de problemas físicos. Imagine que las tablas físicas tienen un prefijo similar (y use mayúsculas para esto):
- REF_ para referencia (como Usuario) y tablas de búsqueda
- BUL_ para el sistema de boletines
.
¿No puedo nombrar las tablas con letras mayúsculas? No estoy seguro de por qué. No sé por qué no puedo tener nombres de tablas en mayúsculas. ¿Tiene que ver con el uso de las tablas de la base de datos de MyIsam?

.
22. rank (todos) se pueden derivar directamente de la base de datos (recuerde, no se preocupe por el código durante el modelado de datos). Si lo almacena, es un error de Normalización; una columna duplicada; que debe mantenerse actualizado; que puede desincronizarse con el valor derivado; que se llama una anomalía de actualización. La quinta forma normal elimina las anomalías de actualización. Ese es mi nivel mínimo de Normalización, así que eso es lo que obtendrás de mí.

22.1. No estoy interfiriendo en absoluto con el orden de clasificación o el tema de la popularidad; de hecho, por lo que parece, no ha cerrado esa funcionalidad. Solo estoy tomando datos redundantes, la columna de rango , fuera, como parte del proceso de Normalización.

22.2. Aquí hay un ▶Tutorial rápido◀ en el operador RANK() (como se le conoce comúnmente). No es ANSI SQL; es una extensión de Oracle y MS. Sin embargo, no es necesario si comprende las Subconsultas, razón por la cual Sybase no lo tiene. Dudo que MySQL lo tenga, así que necesitas entenderlo. Comprender las subconsultas escalares es un requisito previo. Sintaxis de Sybase, así que introduzca los puntos y comas, etc. Siéntase libre de hacer preguntas específicas.
.
Nunca había visto ese enfoque de escribir Rank =(SELECT.... ¿Es eso lo mismo que (SELECCIONAR ...) como Rango?

.
22.3. Necesitar entender por qué, no es problema en absoluto. Los niños únicos siguen ciegamente reglas simples, y usted ciertamente no es uno de ellos.
.
23. Confirmado:users.total_bulletins es redundante; se puede derivar. Eliminado.
.
24. Todos sus PK son Id. ¿Aún no te has cansado de perderte en el código? Olvídate de pegar Id iot PKs en todo lo que se mueve, averigüemos cómo sus usuarios Identificar sus Entidades; qué Entidades son verdaderamente Independientes, y las otras que dependen de Entidades Independientes.

24.1. Nunca use Id o cualquier forma de este tipo. Cuando sea un PK, utilice el formulario completo.

24.2. Llame a location_id, location_id, donde sea que esté, incluida la tabla PK. La excepción es cuando necesita mostrar el rol. Esto quedará claro en el Modelo de Datos.
.
25. No tiene integridad referencial declarativa, no definida Llaves extranjeras. Esa es una mala noticia por muchas razones diferentes. Una vez que se aclaren estas preguntas, agréguelas. DRI significa que tanto como sea posible, si no toda, la integridad se declara en SQL. El estándar ISO/IEC/ANSI SQL permite esto, pero el extremo del mercado de software gratuito no proporciona el estándar y se está poniendo al día lentamente. Significa que el servidor no permitirá que se agregue una fila en la tabla FK a menos que el PK exista en la tabla principal. MySQL proporcionó recientemente DRI para claves externas. Para FK, consulte ▶ este artículo◀ .

25.1. Para las restricciones CHECK y RULES, deberá implementarlas en el código.

mis claves foráneas son como, users-id(fk) =users.id(pk) No estoy seguro de cómo agregarlas aparte de lo que he hecho, pero ciertamente lo haré una vez que sepa cómo hacerlo.

Veinticinco. Comentarios anotados. No soy un especialista en MySQL. Sí, esos son los problemas que tienes que resolver por ti mismo. En general, según mi lectura, MySQL no tiene piernas; para cualquier cosa SQL-ish, necesita InnoDB.

.
27. Dado:He repensado los requisitos de clasificación para el boletín. Los usuarios pueden ordenar cronológicamente, fácil, tiene sentido. Los usuarios pueden ordenar los boletines por la fecha de la última respuesta al boletín. Entonces podemos olvidarnos del rango y debería ser realmente fácil ordenar los boletines cronológicamente por el momento de su última respuesta. ¿Cuáles son tus pensamientos?

Problemas abiertos

(Cero)

Modelo de datos

Bien, asumiendo que no tiene problemas con el ERD e implementando todos los problemas cerrados, modelé los datos y preparé un quinto modelo de datos 09 dic 10 para su revisión. Definitivamente necesito mucho más comentarios, preguntas, etc., sobre esto. Tengo dificultad para aceptar que ya está hecho. Probablemente sea mejor comenzar a escribir código real para sus áreas problemáticas.

Enlaces

▶Enlace a notación IDEF1X◀ Realmente necesita leer y comprender esto antes de leer el modelo de datos.

▶Enlace a los datos del quinto boletín Modelo◀ El diagrama de relación de entidad está en la primera página, seguido del Modelo de datos .

  • Las claves son IDEF1X bastante directas (excepto por UserId que proporcioné como contrapunto); lo que significa monedero de claves relacionales. No mejorado y no optimizado para consideraciones físicas. Antes de rechazarlos, primero obsérvelos, regístrelos y evalúelos. Por supuesto que podemos añadir Id iot keys, pero antes de hacer eso, asegurémonos de entender lo que vamos a perder.

  • Observe los Identificadores (líneas continuas) según el documento de Notación. La columna vertebral, la vértebra del sistema es Location ... Bulletin ... Response .

  • Tenga en cuenta que Keys en realidad implementa muchas reglas comerciales.

  • Observe la jerarquía natural que he representado. Vea si tiene algún significado para usted.

  • Las frases verbales son realmente importantes; mira si significan algo.

Comentarios sobre el primer modelo de datos y respuestas

Una pregunta que tengo es que la clave principal de la ubicación se usará para formar la clave principal secundaria (están unidas por una línea sólida). Realmente no entiendo ese concepto

  • ¿Qué es un buen identificador para boletín? , qué usan naturalmente sus usuarios para identificar un boletín...
  • "¿Has visto el boletín de Virginia FO ayer?",
  • "Sally de Washington escribe buenos boletines", etc.

¿o por qué no existe esa relación entre el usuario y el boletín?

  • Según la intención indicada más arriba, dado que ahora he mostrado la calificación como una tabla y cuál sería la representación, una vez, la eliminaré

  • Creo que Permiso debería ser una Entidad.

  • Bulletin PK ahora es (StateCode, Town, UserId, SequenceNo) . Para ser claros, SequenceNo está dentro de StateCode, Town, UserId :serán las 5 para el quinto boletín de Sally sobre MO/Billngs FO.

  • Tenga en cuenta que la configuración del usuario BulletinsPerPage ,etc, son 1::1 con User , por lo que están en User; la tabla secundaria sería incorrecta.

  • Errores tipográficos corregidos.

Comentarios sobre el segundo modelo de datos y respuestas

  • Los PK para ambos Bulletin y Response han sido cambiados para reflejar (7). BulletinNo y ResponseNo han sido reemplazados por BulletinDate y ResponseDate (que solía ser CreatedDate ), para permitir múltiples respuestas por User por Bulletin .

Comentarios sobre el tercer modelo de datos y respuestas

Confía en que tuviste un buen descanso.

  1. Hace al menos 30 años (que yo sepa), los gigantes de la industria tenían este debate. Los nombres son siempre singulares. Las tablas son sustantivos. Las frases verbales son verbos. Esto no se limita a las convenciones de nomenclatura de db, se aplica a documentos, tesis, disertaciones, etc. Puede tener 5 conclusiones al final del documento, pero la sección o el título del capítulo, tanto en el ToC como en la parte superior de la página es "Conclusión".

    Después de luchar contra ellos durante toda la universidad, tan pronto como comencé mi primer trabajo de programación remunerado y vi la importancia de las reglas en el mundo real, a diferencia de los argumentos teóricos que teníamos en la universidad, lo dejé como un desperdicio. de tiempo. Todo ese tiempo y energía que desperdicié fue liberado para hacer un trabajo productivo. Desde entonces, no cuestiono a los gigantes; solo acepto Que sus mentes son más grandes que las mías. Es como aceptar las Normas, o comportarse dentro de la ley, o de Dios. No tengo muy, muy buenas razones para hacer algo ilegal.

    De todos modos, la facilidad de lenguaje (discusión, SQL, documentación) que es compatible con tales reglas no puede explicarse adecuadamente; a medida que escriba más y más código SQL, se volverá más claro.

    Siempre eres libre de usar lo que quieras. Solo entrego en singular.

  2. Bien por mi.

    Pero debe tener en cuenta que esos dos elementos, en la secuencia identificada (un índice único no PK o clave alternativa) se requieren universalmente para establecer la singularidad de una persona. Eliminarlos resultará en dos cosas. Primero, ya no podrá identificar la unicidad entre Users (y por lo tanto puede tener filas duplicadas). En segundo lugar, el AK se vuelve no único, una entrada de inversión.

  3. El punto es (al contrario de una de las publicaciones), cualquier columna que sea 1::1 con el User PK, debe residir en User . Todos los ajustes de preferencias. Desde que limpiamos las InterestedLocations y InterestedCategories , solo conozco BulletinsPerPage restante; pero estoy seguro de que hay otros. IsPreference2 es un ej. de un booleano;NumPreference3 es un ej. de un entero. Etc. Puede decirme cuáles son las Preferencias reales.

    (Probemos eso en plural:... cualquier columna que sea 1::1 con los Users PK, debe residir en Users . Simplemente no lo hace por mí, me obsesiono con el inglés roto y soy un poco preciado con mi lengua materna).

    Modelo de datos actualizado.

  4. Excelente. Avísame cuando te sientas cómodo con eso y te daré el modelo físico.

    ¿Qué hay de las frases verbales?

Comentarios del 6 de diciembre de 10 a las 20:38 EST (pequeñas actualizaciones)

.
28. Donde solo hay una aparición de PK como FK, por supuesto, el nombre de la columna FK es el mismo que el nombre de la columna PK. Sin embargo, cuando hay más de una occ del FK (echa un vistazo a ResponseRating ), hay tres UserIds ), necesitamos diferenciarlos. En la terminología IDEF1X, esto se denomina Roles. El Rol del User quién emitió el Bulletin es Issuer , y así. Obviamente, es mejor usar ese nombre y mantenerlo consistente en toda la jerarquía (no UserId en Bulletin y luego cuando lleguemos a Response , donde hay dos y se exige una diferenciación, cámbielo a IssuerId . Pensé que podrías tener un problema con eso; en las primeras etapas, el uso es Issuer.UserId para que quede absolutamente claro que es UserId como FK, y el Rol es Issuer; cuando llegamos al modelo físico, se simplifica a IssuerId .

Del mismo modo, tenemos muchas columnas DateTime (Date para abreviar si lo desea; de lo contrario, Dtm), que deben diferenciarse.
.
29. ¿La notación IDEF1X no tenía sentido?

  • El PK de cada tabla está arriba de la línea, en el orden especificado.
  • Recuerde que llevamos las PK de las tablas principales de todos modos y, si tiene sentido, usar esas FK para formar la PK secundaria.
  • Para Bulletin :

    • La ubicación FK (StateCode, Town) para el que se emite
    • El UserId del Emisor
    • y la fecha y hora en que se emitió, para que sea único.
    • por lo tanto (StateCode, Town, IssuerId, BulletinDate)`
  • Para eliminar todas las ResponseRatings para este Bulletin , utilice WHERE = sobre esos cuatro Bulletin columnas

.
30. Porque (State, Town) es el PK de Location , llevando donde sea. Y forma parte del Bulletin PK, por lo que cualquier tabla dependiente lleva esas columnas porque llevan el Bulletin PAQUETE.

Previamente habíamos identificado que (State, Town) es el PK, lo dejaré como está Consulte (38) para cambiar.

.
33. Vale la pena discutir. Sí, si lo va a mostrar cuando (por ejemplo) muestre Responses y los usuarios entienden UserName . No, si tiene 30 bytes y también hay un UserId único de 4 bytes . La idea es tomar estas decisiones conscientemente, consciente de lo que está renunciando, cuando eventualmente decida que una clave de 30 bytes de 6 columnas es demasiado engorrosa para migrar a los niños.

  • Dije al principio que usaría UserId como un típico Id Pk, porque se transporta/migra a varias tablas secundarias.
  • Podemos dejar cómo se crea para más adelante. Pero es una PK sustituta pura.

.
34. No hay problema. Category ya lo tiene Cambiaré Order a ListOrder .

.
35. Por supuesto. Según lo que he leído y escuchado, estoy muy contento con él. Pero me gustaría más ida y vuelta para lograr cierta confianza, antes de escribir el código. Alternativamente, véalo como una experiencia de aprendizaje y acepte que el modelo y el código pueden cambiar más adelante. ¿Le gustaría que produzca el Físico ahora? Si me da alguna y todas las correcciones, publicaré la próxima versión. Espero preferencias en User . Además, ejecute rápidamente las funciones y compruebe que tiene todas las columnas que necesita.

Mire algunas de las otras respuestas, con el propósito de aprender e interesar.

.
36. Uniones. Solo únete en cuatro tres columnas en lugar de una. SQL es engorroso con las uniones, y la nueva sintaxis que se suponía que lo haría más fácil, en realidad es más engorrosa. Mis codificadores nunca escriben uniones:ahorramos tiempo y errores tipográficos. Tengo un proceso que, dadas dos o más tablas, generará el código con todas las columnas y uniones. No sé lo suficiente de MySQL para convertir eso por ti.

Modelo de datos actualizado.
.

Comentarios sobre el 8 de diciembre de 10 a las 20:49, cuarto modelo de datos y respuestas

.
Consulte la sección anterior inmediatamente anterior, hay pequeñas actualizaciones.

IDEF1X:Tu velocidad está bien.

Tenga en cuenta que el niño siempre "hereda" el Parent PK, como un FK (ya sea línea continua o discontinua), de lo contrario, no hay relación entre ellos. Al usar estas columnas que existen en el niño de todos modos, para formar el niño PK, llevamos el significado (y esa es la diferencia entre sólido y roto). Y así no necesitamos buscar un Identificador independiente para el niño. El poder relacional de este método se aclarará más adelante, cuando esté programando.

El apartado que nos ocupa es sobre Identificadores :natural vs antinatural; significativo vs sin sentido. Más adelante verá cómo podemos usar la capacidad relacional del motor, cuando la PK secundaria se forma a partir de la PK principal. (¿Tu apellido no es el mismo que el de tu padre?)

También es importante comprender las bases de datos relacionales y su capacidad. Eso se pierde cuando nos acercamos a la base de datos (por ejemplo) desde una perspectiva orientada a objetos y la tratamos como una ubicación para hacer que nuestras clases sean "persistentes". Por lo tanto, intentaremos aprender y usar términos relacionales. Se vuelve difícil cuando vas a Francia y esperas que hablen americano y usen la misma moneda; aprende a hablar 10 palabras en francés, y te recibirán con los brazos abiertos, y tendrás una experiencia bastante diferente con los lugareños.

De todos modos, adelante con la implementación del modelo. Solo date cuenta de que probablemente haremos un cambio en algún momento. Guarde todo su DDL. Guarde todos sus datos de prueba como declaraciones de inserción o como copia de seguridad de tabla o exportación de formato de caracteres (no tengo idea de lo que MySQL puede/no puede hacer en esta área).
37.1. Manejada, la relación n::n con Office &Category . Solo "verás" eso cuando lleguemos al modelo físico.

37.2. Listo.

37.3 Listo.
.
38. Excelente. Más corto también. Tenga en cuenta que nunca podrán tener dos Offices en el mismo código postal. NUMERIC(5,0) es bueno, pero pensé que EE. UU. se estaba moviendo hacia los 7 dígitos. No importa, puedes resolverlo; es un PK excelente para Office . Ahora esta columna, que formaba parte de Address , probablemente ZipCode , ha sido elevado a un propósito superior, sin duplicación; dado que lo llevamos en 5 tablas secundarias y queremos que el nombre de PK sea claro, según las convenciones explicadas anteriormente, lo llamaremos OfficeCode; OfficeZipCode podría ser una tontería.

Necesitamos un índice único en Name para asegurarse de que no agreguen dos Offices con el mismo nombre. Tenga en cuenta que, con fines explicativos, esta es en realidad la clave lógica de Office , reemplazando (StateCode, Town) , y sigue siendo así.

Sigo pensando que puede necesitar StateCode y Town como una referencia rápida (aparte de sentarse en algún lugar en Address )

Modelo de datos actualizado, Fifth ahora disponible para revisión. No indicó su preferencia, por ...Date vs ...Dtm . Me quedo con este último, ya que es más específico, identificando también el componente de tiempo. Fácil de cambiar.

Esta respuesta ha alcanzado la longitud máxima. Continúa en la "Parte II"