Mis requerimientos son:
- Necesita poder agregar dinámicamente campos definidos por el usuario de cualquier tipo de datos
- Necesita poder consultar UDF rápidamente
- Necesita poder hacer cálculos en UDF basados en el tipo de datos
- Necesita poder ordenar UDF según el tipo de datos
Otra información:
- Estoy buscando rendimiento principalmente
- Hay algunos millones de registros maestros que pueden tener datos UDF adjuntos
- La última vez que lo verifiqué, había más de 50 mil registros UDF en nuestra base de datos actual.
- La mayoría de las veces, un UDF solo se adjunta a unos pocos miles de registros maestros, no a todos
- Los UDF no se unen ni se usan como claves. Son solo datos utilizados para consultas o informes
Opciones:
Cree una tabla grande con StringValue1, StringValue2 ... IntValue1, IntValue2, ... etc. Odio esta idea, pero consideraré si alguien me puede decir que es mejor que otras ideas y por qué.
Cree una tabla dinámica que agregue una nueva columna a pedido según sea necesario. Tampoco me gusta esta idea, ya que siento que el rendimiento sería lento a menos que indices cada columna.
Cree una sola tabla que contenga UDFName, UDFDataType y Value. Cuando se agrega un nuevo UDF, genere una Vista que extraiga solo esos datos y los analice en el tipo especificado. Los elementos que no cumplen los criterios de análisis devuelven NULL.
Cree múltiples tablas UDF, una por tipo de datos. Entonces tendríamos tablas para UDFStrings, UDFDates, etc. Probablemente haría lo mismo que # 2 y generaría automáticamente una Vista cada vez que se agregue un nuevo campo
Tipos de datos XML? No he trabajado con estos antes, pero los he visto mencionados. No estoy seguro de si me darían los resultados que quiero, especialmente con el rendimiento.
¿Algo más?
Respuestas:
Si el rendimiento es la principal preocupación, iría con el # 6 ... una tabla por UDF (realmente, esta es una variante del # 2). Esta respuesta está específicamente diseñada para esta situación y la descripción de la distribución de datos y los patrones de acceso descritos.
Pros:
Debido a que indica que algunos UDF tienen valores para una pequeña porción del conjunto de datos general, una tabla separada le brindará el mejor rendimiento porque esa tabla será tan grande como sea necesario para admitir el UDF. Lo mismo es válido para los índices relacionados.
También obtiene un aumento de velocidad al limitar la cantidad de datos que deben procesarse para agregaciones u otras transformaciones. Dividir los datos en varias tablas le permite realizar algunos de los análisis estadísticos agregados y de otro tipo en los datos UDF, luego unir ese resultado a la tabla maestra a través de una clave externa para obtener los atributos no agregados.
Puede usar nombres de tabla / columna que reflejen cuáles son realmente los datos.
Tiene control completo para usar tipos de datos, verificar restricciones, valores predeterminados, etc. para definir los dominios de datos. No subestimes el impacto en el rendimiento resultante de la conversión de tipos de datos sobre la marcha. Tales restricciones también ayudan a los optimizadores de consultas RDBMS a desarrollar planes más efectivos.
Si alguna vez necesita utilizar claves foráneas, la integridad referencial declarativa incorporada rara vez se ve superada por la aplicación de restricciones basadas en disparadores o en el nivel de aplicación.
Contras:
Esto podría crear muchas tablas. Hacer cumplir la separación de esquemas y / o una convención de nombres aliviaría esto.
Se necesita más código de aplicación para operar la definición y gestión de UDF. Espero que esto sea aún menos código necesario que para las opciones originales 1, 3 y 4.
Otras Consideraciones:
Si hay algo sobre la naturaleza de los datos que tenga sentido para que se agrupen los UDF, eso debería alentarse. De esa manera, esos elementos de datos se pueden combinar en una sola tabla. Por ejemplo, supongamos que tiene UDF para color, tamaño y costo. La tendencia en los datos es que la mayoría de las instancias de estos datos parecen
más bien que
En tal caso, no incurrirá en una penalización de velocidad notable al combinar las 3 columnas en 1 tabla porque pocos valores serían NULOS y evitará crear 2 tablas más, que son 2 uniones menos necesarias cuando necesita acceder a las 3 columnas .
Si llega a un muro de rendimiento desde un UDF que está muy poblado y se usa con frecuencia, entonces se debe considerar su inclusión en la tabla maestra.
El diseño lógico de la tabla puede llevarlo a un cierto punto, pero cuando los recuentos de registros se vuelven realmente masivos, también debe comenzar a ver qué opciones de partición de tabla proporciona su RDBMS de elección.
fuente
He escrito sobre este problema mucho . La solución más común es el antipatrón Entity-Attribute-Value, que es similar a lo que usted describe en su opción # 3. Evita este diseño como la peste .
Lo que uso para esta solución cuando necesito campos personalizados verdaderamente dinámicos es almacenarlos en un blob de XML, para poder agregar nuevos campos en cualquier momento. Pero para hacerlo más rápido, también cree tablas adicionales para cada campo en el que necesite buscar u ordenar (no tiene una tabla por campo, solo una tabla por campo de búsqueda ). Esto a veces se denomina diseño de índice invertido.
Puede leer un artículo interesante de 2009 sobre esta solución aquí: http://backchannel.org/blog/friendfeed-schemaless-mysql
O puede usar una base de datos orientada a documentos, donde se espera que tenga campos personalizados por documento. Yo elegiría Solr .
fuente
fieldname
otablename
es almacenar identificadores de metadatos como cadenas de datos, y ese es el comienzo de muchos de los problemas. Ver también en.wikipedia.org/wiki/Inner-platform_effectLo más probable es que cree una tabla con la siguiente estructura:
Los tipos exactos de curso dependen de sus necesidades (y, por supuesto, de los dbms que está utilizando). También puede usar el campo NumberValue (decimal) para int's y booleans. Es posible que también necesite otros tipos.
Necesita algún enlace a los registros maestros que poseen el valor. Probablemente sea más fácil y rápido crear una tabla de campos de usuario para cada tabla maestra y agregar una clave foránea simple. De esta forma, puede filtrar registros maestros por campos de usuario de forma fácil y rápida.
Es posible que desee tener algún tipo de información de metadatos. Entonces terminas con lo siguiente:
Tabla UdfMetaData
Table MasterUdfValues
Hagas lo que hagas, no cambiaría la estructura de la tabla dinámicamente. Es una pesadilla de mantenimiento. También me no utilizo estructuras XML, son demasiado lentos.
fuente
Esto suena como un problema que podría resolverse mejor con una solución no relacional, como MongoDB o CouchDB.
Ambos permiten la expansión del esquema dinámico al tiempo que le permiten mantener la integridad de tupla que busca.
Estoy de acuerdo con Bill Karwin, el modelo EAV no es un enfoque eficaz para usted. Usar pares de nombre-valor en un sistema relacional no es intrínsecamente malo, pero solo funciona bien cuando el par de nombre-valor forma una tupla completa de información. Cuando lo usa, lo obliga a reconstruir dinámicamente una tabla en tiempo de ejecución, todo tipo de cosas comienzan a ponerse difíciles. La consulta se convierte en un ejercicio de mantenimiento de pivote o lo obliga a empujar la reconstrucción de la tupla hacia la capa del objeto.
No puede determinar si un valor nulo o faltante es una entrada válida o falta de entrada sin incrustar reglas de esquema en su capa de objeto.
Pierde la capacidad de administrar eficientemente su esquema. ¿Es un varchar de 100 caracteres el tipo correcto para el campo "valor"? 200 caracteres? ¿Debería ser nvarchar en su lugar? Puede ser un compromiso difícil y uno que termina con la necesidad de establecer límites artificiales en la naturaleza dinámica de su conjunto. Algo así como "solo puede tener x campos definidos por el usuario y cada uno solo puede tener y caracteres de longitud.
Con una solución orientada a documentos, como MongoDB o CouchDB, mantiene todos los atributos asociados con un usuario dentro de una sola tupla. Dado que las uniones no son un problema, la vida es feliz, ya que a ninguno de estos dos les va bien a pesar de la exageración. Sus usuarios pueden definir tantos atributos como quieran (o lo permitirá) en longitudes que no serán difíciles de administrar hasta que alcance aproximadamente 4 MB.
Si tiene datos que requieren integridad de nivel ACID, podría considerar dividir la solución, con los datos de alta integridad que viven en su base de datos relacional y los datos dinámicos que viven en un almacén no relacional.
fuente
Incluso si proporciona un usuario que agrega columnas personalizadas, no será necesariamente el caso que las consultas en esas columnas funcionen bien. Hay muchos aspectos que intervienen en el diseño de consultas que les permiten tener un buen desempeño, el más importante de los cuales es la especificación adecuada de lo que debe almacenarse en primer lugar. Por lo tanto, fundamentalmente, ¿es que desea permitir a los usuarios crear esquemas sin pensar en las especificaciones y poder obtener rápidamente información de ese esquema? Si es así, es poco probable que cualquier solución de este tipo se adapte bien, especialmente si desea permitir que el usuario realice un análisis numérico de los datos.
Opción 1
En mi opinión, este enfoque le brinda un esquema sin conocimiento de lo que significa el esquema, que es una receta para el desastre y una pesadilla para los diseñadores de informes. Es decir, debe tener los metadatos para saber qué columna almacena qué datos. Si esos metadatos se confunden, tiene el potencial de manipular sus datos. Además, facilita colocar los datos incorrectos en la columna incorrecta. ("¿Qué? String1 contiene el nombre de conventos? Pensé que eran las drogas favoritas de Chalie Sheen")
Opción 3,4,5
OMI, los requisitos 2, 3 y 4 eliminan cualquier variación de un EAV. Si necesita consultar, ordenar o hacer cálculos sobre estos datos, un EAV es el sueño de Cthulhu y la pesadilla de su equipo de desarrollo y DBA. Los EAV crearán un cuello de botella en términos de rendimiento y no le brindarán la integridad de los datos que necesita para obtener rápidamente la información que desea. Las consultas se convertirán rápidamente en nudos cruzados de Gordian.
Opción 2,6
Eso realmente deja una opción: reunir especificaciones y luego construir el esquema.
Si el cliente desea el mejor rendimiento en los datos que desea almacenar, debe pasar por el proceso de trabajar con un desarrollador para comprender sus necesidades de modo que se almacene de la manera más eficiente posible. Todavía podría almacenarse en una tabla separada del resto de las tablas con código que crea dinámicamente un formulario basado en el esquema de la tabla. Si tiene una base de datos que permite propiedades extendidas en columnas, incluso podría usarlas para ayudar al creador de formularios a usar etiquetas agradables, información sobre herramientas, etc., de modo que todo lo que se necesita es agregar el esquema. De cualquier manera, para crear y ejecutar informes de manera eficiente, los datos deben almacenarse correctamente. Si los datos en cuestión tendrán muchos nulos, algunas bases de datos tienen la capacidad de almacenar ese tipo de información. Por ejemplo,
Si esto fuera solo una bolsa de datos en la que no se realizaría ningún análisis, filtrado u ordenación, diría que alguna variación de un EAV podría ser el truco. Sin embargo, dados sus requisitos, la solución más eficiente será obtener las especificaciones adecuadas, incluso si almacena estas nuevas columnas en tablas separadas y crea formularios dinámicamente a partir de esas tablas.
Columnas dispersas
fuente
Según mi investigación, varias tablas basadas en el tipo de datos no te ayudarán en el rendimiento. Especialmente si tiene datos masivos, como 20K o 25K registros con más de 50 UDF. El rendimiento fue lo peor.
Debe ir con una sola tabla con varias columnas como:
fuente
Esta es una situación problemática, y ninguna de las soluciones parece "correcta". Sin embargo, la opción 1 es probablemente la mejor tanto en términos de simplicidad como de rendimiento.
Esta es también la solución utilizada en algunas aplicaciones empresariales comerciales.
EDITAR
Otra opción que está disponible ahora, pero no existía (o al menos no estaba madura) cuando la pregunta original se hizo es utilizar campos json en la base de datos.
muchos DB relacionales ahora admiten campos basados en json (que pueden incluir una lista dinámica de subcampos) y permiten consultarlos
postgreso
mysql
fuente
He tenido experiencia con 1, 3 y 4 y todos terminan siendo desordenados, ya que no está claro cuáles son los datos o realmente complicado con algún tipo de categorización suave para dividir los datos en tipos dinámicos de registro.
Me sentiría tentado a probar XML, debería poder aplicar esquemas contra el contenido del xml para verificar la escritura de datos, etc., lo que ayudará a mantener conjuntos de datos UDF diferentes. En las versiones más recientes del servidor SQL, puede indexar en campos XML, lo que debería ayudar en el rendimiento. (ver http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspx ) por ejemplo
fuente
Si está utilizando SQL Server, no pase por alto el tipo sqlvariant. Es bastante rápido y debería hacer tu trabajo. Otras bases de datos pueden tener algo similar.
Los tipos de datos XML no son tan buenos por razones de rendimiento. Si está haciendo cálculos en el servidor, tendrá que deserializarlos constantemente.
La opción 1 suena mal y se ve cruda, pero en cuanto al rendimiento puede ser su mejor opción. He creado tablas con columnas llamadas Field00-Field99 antes porque simplemente no puedes superar el rendimiento. Es posible que también deba considerar su rendimiento INSERT, en cuyo caso este también es el indicado. ¡Siempre puede crear Vistas en esta tabla si desea que se vea ordenada!
fuente
SharePoint usa la opción 1 y tiene un rendimiento razonable.
fuente
He logrado esto con mucho éxito en el pasado sin usar ninguna de estas opciones (opción 6? :)).
Creo un modelo para que jueguen los usuarios (almacenar como xml y exponer a través de una herramienta de modelado personalizada) y de las tablas y vistas generadas por el modelo para unir las tablas base con las tablas de datos definidas por el usuario. Por lo tanto, cada tipo tendría una tabla base con datos básicos y una tabla de usuario con campos definidos por el usuario.
Tome un documento como ejemplo: los campos típicos serían nombre, tipo, fecha, autor, etc. Esto iría en la tabla principal. Luego, los usuarios definirían sus propios tipos de documentos especiales con sus propios campos, como contract_end_date, renewal_clause, blah blah blah. Para ese documento definido por el usuario, habría la tabla de documentos principales, la tabla xcontract, unida en una clave primaria común (por lo que la clave primaria xcontracts también es ajena a la clave principal de la tabla principal). Entonces generaría una vista para envolver estas dos tablas. El rendimiento cuando las consultas fueron rápidas. También se pueden incrustar reglas comerciales adicionales en las vistas. Esto funcionó muy bien para mí.
fuente
Nuestra base de datos utiliza una aplicación SaaS (software de servicio de asistencia) donde los usuarios tienen más de 7k "campos personalizados". Utilizamos un enfoque combinado:
(EntityID, FieldID, Value)
mesa para buscar los datosentities
tabla, que contiene todos los valores de entidad, utilizados para mostrar los datos. (de esta manera no necesita un millón de JOIN para obtener los valores de los valores).Podría dividir aún más el # 1 para tener una "tabla por tipo de datos" como esta respuesta sugiere , de esta manera incluso puede indexar sus UDF.
PD: Un par de palabras para defender el enfoque de "Entidad-Atributo-Valor" que todos siguen criticando. Hemos usado # 1 sin # 2 durante décadas y funcionó bien. A veces es una decisión comercial. ¿Tiene tiempo para reescribir su aplicación y rediseñar el db o puede gastar un par de dólares en servidores en la nube, que son realmente baratos en estos días? Por cierto, cuando estábamos usando el enfoque n. ° 1, nuestra base de datos tenía millones de entidades, a las que accedían cientos de miles de usuarios, y un servidor db de doble núcleo de 16 GB funcionaba bien
fuente
custom_fields
tabla que almacena valores como 1 =>last_concert_year
, 2 =>band
, 3 =>music
y luego unacustom_fields_values
tabla con valores 001, 1, 1976 002, 1, 1977 003, 2,Iron Maiden
003, 3 ¡Metal
Espero que el ejemplo tenga sentido para ti y perdón por el formato!bands
tabla con una fila,1,'Iron Maiden'
luegocustom_fields
con filas y1,'concert_year' | 2,'music'
luegocustom_fields_values
con filas1,1,'1977'|1,2,'metal'
En los comentarios, lo vi decir que los campos UDF son para volcar datos importados que el usuario no ha asignado correctamente.
Quizás otra opción es rastrear la cantidad de UDF hechas por cada usuario y obligarlos a reutilizar campos diciendo que pueden usar 6 (o algún otro límite igualmente aleatorio) de campos personalizados.
Cuando se enfrenta a un problema de estructuración de la base de datos como este, a menudo es mejor volver al diseño básico de la aplicación (sistema de importación en su caso) y ponerle algunas restricciones más.
Ahora lo que haría es la opción 4 (EDITAR) con la adición de un enlace a los usuarios:
Ahora asegúrese de hacer vistas para optimizar el rendimiento y obtener sus índices correctos. Este nivel de normalización reduce el tamaño de la base de datos, pero su aplicación es más compleja.
fuente
Recomendaría # 4 ya que este tipo de sistema se usó en Magento, que es una plataforma CMS de comercio electrónico altamente acreditada. Use una sola tabla para definir sus campos personalizados usando fieldId y columnas de etiquetas . Luego, tenga tablas separadas para cada tipo de datos y dentro de cada una de esas tablas tenga un índice que indexe por fieldId y las columnas de valor de tipo de datos . Luego, en sus consultas, use algo como:
Esto garantizará el mejor rendimiento posible para los tipos definidos por el usuario en mi opinión.
En mi experiencia, he trabajado en varios sitios web de Magento que atienden a millones de usuarios por mes, alojan miles de productos con atributos de producto personalizados y la base de datos maneja la carga de trabajo fácilmente, incluso para generar informes.
Para generar informes, puede utilizar
PIVOT
para convertir los valores de etiqueta de la tabla Campos en nombres de columna, luego pivotar los resultados de su consulta de cada tabla de tipo de datos en esas columnas pivotadas.fuente