Esta pregunta es sobre cómo debería diseñar una base de datos, pueden ser bases de datos relacionales / nosql, dependiendo de cuál será la mejor solución
Dado un requisito en el que deberá crear un sistema que incluya una base de datos para rastrear "Compañía" y "Usuario". Un solo usuario siempre pertenece a una sola empresa.
- Un usuario solo puede pertenecer a una empresa
- Una empresa puede tener muchos usuarios
El diseño de la tabla "Empresa" es bastante sencillo. La empresa tendrá los siguientes atributos / columnas: (hagámoslo simple)
ID, COMPANY_NAME, CREATED_ON
Primer escenario
Simple y directo, todos los usuarios tienen el mismo atributo, por lo que esto se puede hacer fácilmente en estilo relacional, tabla de usuario:
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON
Segundo escenario
¿Qué sucede si diferentes compañías desean almacenar diferentes atributos de perfil para sus usuarios? Cada compañía tendrá un conjunto definido de atributos que se aplicarán a todos los usuarios de esa compañía.
Por ejemplo:
- La empresa A quiere almacenar: LIKE_MOVIE (boolean), LIKE_MUSIC (boolean)
- La empresa B quiere almacenar: FAV_CUISINE (String)
- La empresa C quiere almacenar: OWN_DOG (boolean), DOG_COUNT (int)
Enfoque 1
La forma de fuerza bruta es tener un esquema único para el usuario y dejar que tengan valores nulos cuando no pertenecen a la empresa:
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, LIKE_MOVIE, LIKE_MUSIC, FAV_CUISINE, OWN_DOG, DOG_COUNT, CREATED_ON
Lo cual es un poco desagradable porque terminará con una gran cantidad de NULLS y filas de usuarios que tienen columnas que no son relevantes para ellos (es decir, todos los usuarios que pertenecen a la Compañía A tienen valores NULL para FAV_CUISINE, OWN_DOG, DOG_COUNT)
Enfoque 2
Un segundo enfoque es tener un "campo de forma libre":
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_1, CUSTOM_2, CUSTOM_3, CREATED_ON
Lo cual sería desagradable por sí solo, ya que no tiene idea de qué son los campos personalizados, el tipo de datos no reflejará los valores almacenados (por ejemplo, almacenaremos el valor int como VARCHAR).
Enfoque 3
He investigado el campo PostgreSQL JSON, en cuyo caso tendrá:
ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_PROFILE_JSON, CREATED_ON
En este caso, ¿cómo podría aplicar diferentes esquemas a un usuario? Un usuario con la empresa A tendrá un esquema similar
{"LIKE_MOVIE":"boolean", "LIKE_MUSIC": "boolean"}
Mientras que un usuario con la Compañía C tendrá un esquema diferente:
{"OWN_DOG ":"boolean", "DOG_COUNT": "int"}
¿Cómo debo resolver este problema? ¿Cómo puedo diseñar la base de datos correctamente para permitir este esquema flexible para un solo "objeto" (Usuario) en función de la relación que tienen (Empresa)?
solución relacional? solución nosql?
Editar: También he pensado en una tabla "CUSTOM_PROFILE" que esencialmente almacenará los atributos del usuario en filas en lugar de columnas.
Hay 2 problemas con este enfoque:
1) Los datos crecen por usuario como filas en lugar de columnas, y esto significa que para obtener una imagen completa del usuario, es necesario realizar muchas uniones, varias uniones a la tabla de "perfil personalizado" en los diferentes atributos personalizados
2) El valor de los datos siempre se almacena como VARCHAR para que sea genérico, incluso si sabemos que se supone que los datos son enteros o booleanos, etc.
fuente
Respuestas:
Por favor considere esto como una alternativa. Los dos ejemplos anteriores requerirán que realice cambios en el esquema a medida que crece el alcance de la aplicación, además de que la solución "custom_column" es difícil de ampliar y mantener. Eventualmente terminarás con Custom_510 y luego imagina lo horrible que será trabajar con esta tabla.
Primero usemos su esquema de Empresas.
A continuación, también usaremos su esquema de Usuarios para los atributos requeridos de nivel superior que serán utilizados / compartidos por todas las compañías.
A continuación, creamos una tabla donde definiremos nuestros atributos dinámicos que son específicos de los atributos de usuario personalizados de cada empresa. Entonces, aquí un valor de ejemplo de la columna Attribute sería "LikeMusic":
A continuación, definimos una tabla de atributos de usuario que contendrá los valores de los atributos del usuario
Esto se puede modificar de muchas maneras para mejorar el rendimiento. Puede usar varias tablas para UserAttributes haciendo que cada una sea específica para el tipo de datos que se almacena en Value o simplemente dejarla como VarChar y trabajar con ella como un almacén de valores clave.
También es posible que desee mover CompanyId fuera de la tabla UserAttributeDefiniton a una tabla de referencia cruzada para futuras pruebas.
fuente
Use una base de datos NoSQL. Habría documentos de la empresa y del usuario. Los usuarios tendrían parte de su esquema creado dinámicamente en función de una plantilla de usuario (texto para indicar campos / tipos para esa empresa).
Así es como podría verse en algo como Firebase.com . Tendría que aprender cómo hacerlo en el que elija.
fuente
Si va a encontrarse con frecuencia con solicitudes de campo personalizadas, en realidad lo modelaría de manera bastante similar a la base de datos. Cree una tabla que contenga los metadatos sobre cada campo personalizado, CompanyCustomField (a quién pertenece, el tipo de datos, etc.) y otra tabla CompanyCustomFieldValues que contiene CustomerId, FieldId y el valor. Si está utilizando algo como Microsoft Sql Server, la columna de valor sería un tipo de datos sql_variant.
Por supuesto, esto no es fácil, ya que necesitará una interfaz que permita a los administradores definir campos personalizados para cada cliente, y otra interfaz que realmente use estos metadatos para crear una interfaz de usuario para recopilar los valores de los campos. Y si tiene otros requisitos, como la agrupación de campos o la necesidad de hacer un tipo de campo de lista de selección, deberá incluir eso con más metadatos / otras tablas (por ejemplo, CompanyCustomFieldPickListOptions).
Esto no es trivial, pero tiene la ventaja de no requerir cambios en la base de datos / cambios de código para cada nuevo campo personalizado. También deberá codificarse cualquier otra característica de los campos personalizados (por ejemplo, si desea regex validar un valor de cadena, o solo permitir fechas entre ciertos rangos, o si necesita habilitar un campo personalizado basado en otro valor de campo personalizado )
fuente
Una alternativa a las otras respuestas es tener una tabla llamada profile_attrib, o similar, para que su aplicación administre completamente el esquema.
A medida que se agregan atributos personalizados
ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1)
, usted puede prohibir eliminarlos. Esto minimizaría su unión, al tiempo que proporciona flexibilidad.Supongo que la compensación es que la aplicación ahora necesita alterar los privilegios de la tabla para la base de datos, y debe ser inteligente para desinfectar los nombres de las columnas.
fuente
[^\w-]+
debería hacerlo bastante bien, sin permitir nada que no0-9A-Za-z_-
sea así, pero sí, la desinfección es imprescindible aquí para protegerse contra la malicia o la estupidez.Su pregunta tiene muchas soluciones potenciales. Una solución es almacenar los atributos adicionales como XML. El XML se puede almacenar como texto o si está utilizando una base de datos que admite tipos XML como XML (SQL Server). El almacenamiento como texto limita su capacidad de consulta (como buscar en un atributo personalizado), pero si el almacenamiento y la recuperación es todo lo que necesita, entonces es una buena solución. Si necesita consultar, almacenar el XML como un tipo XML sería una mejor opción (aunque esto es más específico del proveedor).
Esto le dará a uno la capacidad de almacenar cualquier número de atributos para un cliente con solo agregar una columna de adición en la tabla de clientes. Se podrían almacenar los atributos como hashset o diccionario, se perderá la seguridad de los tipos, ya que todo será una cadena para empezar, pero si se aplica una cadena de formato estándar para fechas, números, booleanos, todo saldrá bien.
Para más información:
https://msdn.microsoft.com/en-us/library/hh403385.aspx
La respuesta de @ WalterMitty también es válida, aunque si uno tiene muchos clientes con diferentes atributos, podría terminar con muchas tablas si sigue el modelo de herencia. Depende de cuántos atributos personalizados se compartan entre los clientes.
fuente
Debería normalizar su base de datos de modo que tenga 3 tablas diferentes para cada tipo diferente de perfil de empresa. Usando su ejemplo, tendría tablas con columnas:
Este enfoque supone que conocerá de antemano la forma de la información que una empresa desea almacenar y que no cambiará con frecuencia. Si se desconoce la forma de los datos en el momento del diseño, probablemente sería mejor usar ese campo JSON o una base de datos nosql.
fuente
Por una razón u otra, las bases de datos son el único campo en el que el efecto de plataforma interna se muestra con mayor frecuencia. Este es solo otro caso de la aparición de antipatrón.
En este caso, estás tratando de luchar contra la solución natural y correcta. Los usuarios de la empresa A no son usuarios de la empresa B, y deben tener sus propias tablas para sus propios campos.
El proveedor de su base de datos no le cobra por la tabla, y no necesita el doble del espacio en disco para el doble de las tablas (de hecho, tener dos tablas es más eficiente porque no almacena los atributos de A para los usuarios de B. Incluso almacenando solo NULL ocupa espacio)
Por supuesto, si hay suficientes campos comunes, puede factorizarlos en una tabla de usuarios compartida y tener una clave externa en cada una de las tablas de usuarios específicas de la compañía. Esta es una estructura tan simple que ningún optimizador de consultas de bases de datos lucha con ella. Cualquier UNIÓN necesaria es trivial.
fuente
Mi solución asume que llamaría a esta consulta desde un programa y que debería poder realizar el procesamiento posterior. Puede tener las siguientes columnas:
CUSTOM_VALUES será de tipo cadena que almacena la clave y el par de valores. la clave será el nombre de la columna y el valor será el valor de la columna, por ejemplo
en estos CUSTOM_VALUES solo guardará la información que exista. Cuando consulta desde el programa, puede dividir esta cadena y usarla.
He estado usando esta lógica y funciona bien, es solo que tendrás que aplicar la lógica de filtrado en el código y no en la consulta.
fuente