2010-07-01 5 views
8

Una aplicación que heredé sigue los resultados de las pruebas de laboratorio realizadas en muestras de materiales. Los datos se almacenan en una sola tabla (tblSampleData) con una clave principal de SampleID y 235 columnas que representan los posibles resultados de las pruebas. El problema es que solo se realizan algunas pruebas por muestra, por lo que cada fila contiene más de 200 nulos. En realidad, hay una segunda tabla similar (tblSampleData2) con otras 215 columnas principalmente nulas y una clave principal de SampleID. Las dos tablas tienen una relación de uno a uno y la mayoría de los SampleID tienen algunos datos en ambas tablas. ¡Para cada SampleID, sin embargo, hay fácilmente 400 columnas nulas!Ahogamiento en un mar de nulos

¿Este es un mal diseño de la base de datos? Si es así, ¿qué norma de forma normal está rota? ¿Cómo puedo consultar esta tabla para identificar qué grupos de columnas suelen rellenarse con datos? Mi objetivo sería tener, digamos 45 tablas con 10 columnas y menos valores nulos. ¿Cómo puedo hacer esto? ¿Cómo evito romper las aplicaciones existentes?

Las tablas tienen alrededor de 200,000 registros de muestra hasta el momento. Los usuarios me piden que agregue más columnas para más pruebas, pero prefiero construir una nueva tabla. ¿Es esto sabio?

+0

¿Qué tipo de consultas está ejecutando esta aplicación en la base de datos? – quantumSoup

+1

CRUD básico. Inserte un nuevo registro de prueba, actualícelo a medida que se completen las pruebas, lea los resultados en gráficos e informes, casi nunca elimine. Las consultas de lectura se realizan en pequeños conjuntos de datos, por cliente. – DeveloperDan

+0

Consulte mi respuesta larga a continuación, pero sería interesante conocer los tipos de datos de las aproximadamente 400 columnas de resultados, específicamente si son todos del mismo * tipo de datos. –

Respuesta

1

No estoy seguro de que el diseño sea realmente tan malo. Los valores NULL en realidad deberían ser relativamente baratos de almacenar. En SQL Server, hay un campo de bits interno (o campos) para cada fila que indica qué valores de columna son NULL.

Si no es necesario mejorar el rendimiento de la aplicación y la relación costo-beneficio de la refactorización debido al cambio del esquema de la tabla no es positiva, ¿por qué cambiarla?

+0

Parece indicar que necesita agregar pruebas de vez en cuando, lo que implica cambiar repetidamente el esquema de la tabla Y cualquier consulta o procedimiento relacionado. Definitivamente hay un costo por eso también. – NYSystemsAnalyst

+0

Todas las respuestas proporcionan información valiosa sobre la normalización y el diseño de la base de datos. Al final, dejé la estructura de la tabla sin cambios y agregué columnas para mis nuevos datos de prueba. Comprenderías mi decisión si miras el código de espagueti de pesadilla en las páginas web del laboratorio (¡no es mi código!). Para refactorizar la aplicación para usar una nueva estructura de tabla, tendría que volver a escribir la aplicación desde cero. Esta fue mi primera pregunta sobre el desbordamiento de pila y me sorprendieron las respuestas rápidas y bien pensadas. ¡Gracias! – DeveloperDan

4

Puede utilizar el bien conocido Entity Attribute Value model (EAV). La descripción de cuándo es apropiado utilizar EAV se ajusta bastante bien con su caso de uso:

representación

Estos datos es análogo a los métodos eficiente del espacio de almacenamiento de una matriz dispersa, donde se almacenan sólo valores no vacíos.

Un ejemplo de modelado de EAV en bases de datos de producción se ve con los hallazgos clínicos (antecedentes, quejas actuales, examen físico, pruebas de laboratorio, investigaciones especiales, diagnósticos) que pueden aplicarse a un paciente. En todas las especialidades de la medicina, estas pueden variar en cientos de miles (con nuevas pruebas que se desarrollan cada mes). La mayoría de las personas que visitan a un médico, sin embargo, tienen relativamente pocos hallazgos.

En su caso específico:

  • la entidad es una muestra de material.
  • El atributo es un tipo de prueba.
  • El valor es el resultado de una prueba para una muestra específica.

EAV tiene algunos inconvenientes graves y crea una serie de dificultades por lo que solo debe aplicarse cuando sea apropiado. No debe usarlo si necesita devolver todos los resultados de la prueba para una muestra específica en una sola fila.

Será difícil modificar la base de datos para usar esta estructura sin interrumpir las aplicaciones existentes.

+1

+1. Si tiene cientos de columnas con nulos en su mayoría, entonces lo está haciendo mal. – tomdemuyt

+0

La migración a EAV facilitará a los usuarios la definición de nuevos atributos, sin tener que modificar la base de datos. – pascal

1

El hecho de que no se rompan las reglas de forma normal no significa que no sea un mal diseño de la base de datos. En general, es mejor tener un diseño con filas más pequeñas y empaquetadas de forma más ajustada, porque de esa manera pueden caber más filas en una página, por lo que hay menos trabajo para la base de datos. Con el diseño actual, el servidor de la base de datos tiene que dedicar mucho espacio para mantener valores nulos.

Evitar que se rompan las aplicaciones existentes es la parte más difícil, si las otras aplicaciones solo necesitan acceso de lectura, podría escribir una vista que se vea idéntica a la anterior.

9

He visto artículos/documentos que indican que simplemente tener NULL en la base de datos rompe la primera forma normal.

De lo que he reunido a partir de su descripción de la base de datos, un mejor diseño podría ser la siguiente:

una tabla de muestra con campos que siempre están asociados con una muestra. Por ejemplo,

Sample 
------ 
SampleID 
SampleDate 
SampleSource 

Luego, una tabla de tipos de prueba con una entrada para cada tipo de prueba que se puede realizar.

TestType 
-------- 
TestTypeID 
TestName 
MaximumAllowedValue 

Finalmente, tienen una tabla intermedia que representa la relación de muchos a muchos entre las dos tablas anteriores y mantiene los resultados para las pruebas.

TestResult 
---------- 
SampleID 
TestTypeID 
TestResult 

Esto eliminaría los valores nulos porque la tabla TestResult solamente contendría las entradas para las pruebas de que en realidad se aplicaron a cada muestra. Una vez diseñé una base de datos para un propósito casi idéntico a lo que creo que estás haciendo y este es el enfoque que tomé.

+1

+1. También configuré una clave única en TestResult (si es apropiado, a partir de la descripción del problema, creo que sí) en SampleID & TestTypeID. –

+0

Me gusta esta respuesta, pero quiero asegurarme de que la entiendo. ¿Mis 450 columnas actuales se convertirán en 450 filas TestType con TestNames que coincidan con los nombres originales de las columnas de la tabla? Me gusta eso, porque no necesitaría crear una nueva tabla cada vez que se agreguen nuevas pruebas. ¿Tiene esto sentido: podría incluir una tabla de TestGroup para identificar clases o categorías de pruebas similares? La tabla TestType contendría una clave foránea TestGroupID. TestGroupNames representaría lo que previamente pensé que deberían ser nombres de tabla separados. – DeveloperDan

+0

Correcto, las 450 columnas se convertirán en 450 filas en la tabla TestType. Luego, para cada muestra, simplemente tomaría las pruebas que se realizaron para realizar entradas en la tabla TestResult. Esto definitivamente haría que la base de datos sea más fácil de mantener a medida que se agreguen nuevas pruebas. Sí, ciertamente podría incluir una tabla TestGroup como describió. Esto haría mucho más fácil agrupar las pruebas para mostrarlas, como en los informes. Como Carl mencionó en su comentario, asegúrese de establecer sus claves y restricciones correctamente para evitar la duplicación de las entradas de resultados de prueba. – NYSystemsAnalyst

1

Si cambia la estructura de su tabla, recomendaría tener una vista llamada tblSampleData que devuelve los mismos datos que la tabla ahora. Eso mantendrá cierta compatibilidad.

+0

Puede ser conveniente refactorizar la aplicación de todos modos, pero esto evitará que la aplicación se rompa inicialmente. –

0

I d Go con 1 mesa principal, donde tendría fila 1 por muestra, que contendría todas las columnas que cada muestra debe tener:

Sample 
------- 
SampleID int auto increment PK 
SampleComment 
SampleDate 
SampleOrigin 
.... 

me gustaría a continuación, añadir una tabla para cada diferentes pruebas o "clase" de pruebas similares, e incluyen todas las columnas relacionadas con los (utilice el nombre de la prueba real y no XYZ):

TestMethod_XYZ 
--------------- 
SampleID int FK Sample.SampleID 
MeltTemp 
BurnTemp 
TestPersonID 
DateTested 
... 

TestMethod_ABC 
--------------- 
SampleID int FK Sample.SampleID 
MinImpactForce 
TestPersonID 
DateTested 
.... 

TestMethod_MNO 
--------------- 
SampleID int FK Sample.SampleID 
ReactionYN 
TimeToReact 
ReactionType 
TestPersonID 
DateTested 
... 

Cuando se busca un resultado, debería buscar la tabla de métodos de prueba que aplica y une de nuevo a la tabla de muestra real.

+0

Has elaborado mi idea original. Sin embargo, esperaba encontrar una consulta inteligente para determinar las clases de exámenes. Es decir, en función de los datos existentes, ¿cuáles son las tablas probables en las que debe dividirse cada columna? Por supuesto, podría pedirle a las personas en el laboratorio que clasifiquen su prueba para mí, pero ¿dónde está la diversión en eso? – DeveloperDan

0

Digamos que tiene la máquina de prueba X con 40 canales de medición. Si sabe que en cada prueba los probadores utilizarán sólo unos pocos canales, se puede cambiar el diseño para:

tblTest: testid, TestDate tblResult: testid, MachineID, channelId, Resultado

Siempre se puede recuperar el diseño anterior usando una tabla cruzada.

0

EAV es una opción pero las consultas te matarán.

¿Es una opción migrar los datos a un DB NoSQL como MongoDB? Creo que esta será la forma más eficiente y fácil de resolver su problema. Como mencionaste que básicamente estás haciendo consultas CRUD, NoSQL debería ser bastante eficiente.

+0

La migración no es probable. Nunca he oído hablar de MongoDB. Lo buscaré en Google y NoSQL. – DeveloperDan

+0

Es una base de datos sin esquema, de la descripción de su problema parece encajar perfectamente: http://www.mongodb.org/. –

0

El diseño actual es pobre. En general, una base de datos con muchos valores NULL es una indicación de diseño deficiente, que viola la 4ta forma normal. Pero el mayor problema con el diseño no es una violación de los principios normales, pero el hecho de que la adición de un nuevo tipo de prueba requiera cambios en la base de datos estructura en lugar de simplemente agregar algunos datos a varias tablas que "definen" una prueba . Peor aún, requiere cambios estructurales en una tabla existente, en lugar de agregar nuevas tablas.

Puede lograr la cuarta forma normal perfecta mediante la adaptación de un sistema de valores-clave tal como lo describen otros. Sin embargo, es posible que pueda mejorar sustancialmente el diseño de la base de datos y aún así mantener su cordura (algo difícil de hacer cuando se trabaja con sistemas de clave y valor sin un ORM) mediante una de las siguientes acciones:

  1. Intento de descubra la mayor cantidad de mediciones requeridas para representar cualquier prueba individual. Si las pruebas arrojan diferentes tipos de datos, deberá descubrir la mayor cantidad de valores de cada tipo de datos devuelto por la prueba más grande. Cree una tabla solo con esas columnas, con la etiqueta Meas1, Meas2, etc. En lugar de 400 columnas necesitará, quizás, 10. O 40. Luego, cree un conjunto de tablas que describan el significado de cada columna para cada prueba. Esta información se puede utilizar para proporcionar indicaciones significativas e informar encabezados de columna en función del tipo de prueba que se almacena. Esto no eliminará los valores NULL por completo, pero los reducirá en gran medida y, siempre que cualquier prueba nueva pueda "ajustarse" al número de mediciones que especificó, se pueden agregar nuevas pruebas como datos en lugar de cambios estructurales.

  2. Descubre la lista real de mediciones para cada prueba y crea una tabla separada para contener los resultados de cada una (información básica como ID de prueba, quién la ejecutó, la hora, etc., sigue en una sola tabla). Este es un patrón de herencia de tablas múltiples (no sé si tiene un nombre real). Aún debe crear una nueva tabla de "datos" para cada nueva prueba, pero ahora no tocará otras tablas de producción existentes y podrá alcanzar la forma normal perfecta.

Espero que esto proporcione algunas ideas para comenzar.

+0

No usamos un ORM. ¿Puede explicar en detalle por qué es difícil mantener el sistema de clave-valor? Consideraré tu idea si es más fácil de mantener, pero no entiendo muy bien la estructura. Intentaré buscar en el patrón de herencia de tablas múltiples, o ¿alguien podría sugerir un enlace o mostrar una estructura de tabla de muestra? – DeveloperDan

1
  1. Es probable que ni siquiera necesita un RDBMS para que estos datos. Almacene sus datos en archivos binarios estructurados o en una tabla DBM/ISAM.

  2. No está normalizado. Generalmente, la falta de normalización es la fuente de todos tus problemas. Pero en este caso, la falta de normalización no es el fin del mundo porque estos datos son de "solo lectura", solo hay una clave, y no está relacionada con ninguna otra cosa. Así que actualizar las anomalías no debería ser una preocupación. Solo debe preocuparse de que los datos originales sean consistentes.

  3. No hay nada demasiado terriblemente mal con todos esos NULL si tratas NULL como un "valor especial" con el mismo significado en toda la aplicación. No se recopilaron los datos. Informacion no disponible. El sujeto se negó a responder la pregunta. Los datos son atípicos. Los datos están pendientes. Se sabe que los datos son DESCONOCIDOS. El sujeto dijo que no sabían ... etc. entiendes la idea. Permitir NULL para no definido razón sin definido lo que significa que es terriblemente incorrecto.

  4. Digo normalize it. Define valores especiales y crea una tabla masiva.O bien, deje NULLs para los programadores de VB y PHP, y divídalos correctamente. Cree una VISTA para unir la copia de seguridad de los datos si necesita admitir el código heredado. Según lo que describió, está hablando de un par de horas de trabajo para corregir esta situación. Eso no es un mal trato.