Heredé una aplicación que asocia muchos tipos diferentes de actividades con un sitio. Hay aproximadamente 100 tipos de actividades diferentes, y cada uno tiene un conjunto diferente de 3-10 campos. Sin embargo, todas las actividades tienen al menos un campo de fecha (podría ser cualquier combinación de fecha, fecha de inicio, fecha de finalización, fecha de inicio programada, etc.) y un campo de persona responsable. Todos los demás campos varían ampliamente y un campo de fecha de inicio no necesariamente se llamará "Fecha de inicio".
Hacer una tabla de subtipos para cada tipo de actividad daría como resultado un esquema con 100 tablas de subtipos diferentes, lo que sería demasiado difícil de manejar. La solución actual a este problema es almacenar los valores de actividad como pares clave-valor. Este es un esquema muy simplificado del sistema actual para transmitir el punto.
Cada actividad tiene múltiples campos de actividad; cada sitio tiene múltiples actividades, y la tabla SiteActivityData almacena los KVP para cada SiteActivity.
Esto hace que la aplicación (basada en la web) sea muy fácil de codificar porque todo lo que realmente necesita hacer es recorrer los registros en SiteActivityData para una actividad determinada y agregar una etiqueta y control de entrada para cada fila a un formulario. Pero hay muchos problemas:
- La integridad es mala; es posible poner un campo en SiteActivityData que no pertenece al tipo de actividad, y DataValue es un campo varchar, por lo que los números y las fechas se deben emitir constantemente.
- Los informes y las consultas ad-hoc de estos datos son difíciles, propensos a errores y lentos. Por ejemplo, obtener una lista de todas las actividades de cierto tipo que tienen una Fecha de finalización dentro de un rango específico requiere pivotes y conversión de varchars a las fechas. Los redactores del informe ODIAN este esquema, y no los culpo.
Entonces, lo que estoy buscando es una forma de almacenar una gran cantidad de actividades que casi no tienen campos en común de una manera que facilite la presentación de informes. Lo que se me ocurrió hasta ahora es usar XML para almacenar los datos de la actividad en un formato pseudo-noSQL:
La tabla de Actividad contendría el XSD para cada actividad, eliminando la necesidad de la tabla de ActivityField. SiteActivity contendría el XML de valor clave, por lo que cada actividad para un sitio ahora estaría en una sola fila.
Una actividad se vería así (pero no la he desarrollado completamente):
<SomeActivityType>
<SomeDateField type="StartDate">2000-01-01</SomeDateField>
<AnotherDateField type="EndDate">2011-01-01</AnotherDateField>
<EmployeeId type="ResponsiblePerson">1234</EmployeeId>
<SomeTextField>blah blah</SomeTextField>
...
Ventajas:
- El XSD validaría el XML, detectando errores como poner una cadena en un campo numérico a nivel de la base de datos, algo que era imposible con el antiguo esquema que almacenaba todo en varchar.
- El conjunto de registros de KVP que se utiliza para crear los formularios web podría reproducirse fácilmente utilizando
select ... from ActivityXML.nodes('/SomeActivityType/*') as T(r)
- Se podría usar una subconsulta xpath del XML para producir un conjunto de resultados que tenga columnas para la fecha de inicio, fecha de finalización, etc. sin usar un pivote, algo así como
select ActivityXML.value('.[@type=StartDate]', 'datetime') as StartDate, ActivityXML.value('.[@type=EndDate]', 'datetime') as EndDate from SiteActivity where...
¿Parece esto una buena idea? No puedo pensar en otras formas de almacenar una cantidad tan grande de diferentes conjuntos de propiedades. Otro pensamiento que tuve fue mantener el esquema existente y traducirlo en algo más fácil de consultar en un almacén de datos, pero nunca antes había diseñado un esquema en estrella y no tenía idea de por dónde empezar.
Pregunta adicional: si defino que una etiqueta tiene un tipo de datos de fecha en el XSD xs:date
, ¿SQL Server la indexará como un valor de fecha? Me preocupa si hago una consulta por fecha, tendrá que convertir la cadena de fecha a un valor de fecha y eliminar cualquier posibilidad de usar un índice.