2011-01-16 11 views
17

Estoy trabajando en un editor que permite a los usuarios crear definiciones de "objetos" en tiempo real. Una definición puede contener cero o más propiedades. Una propiedad tiene un nombre, un tipo. Una vez que se crea una definición, un usuario puede crear un objeto de esa definición y establecer los valores de las propiedades de ese objeto.Esquema para admitir propiedades dinámicas

Con solo un clic del mouse, el usuario debe, por ejemplo. ser capaz de crear una nueva definición llamada "Bicicleta", y agregar la propiedad "Tamaño" de tipo "Numérico". Luego, otra propiedad llamada "Nombre" de tipo "Texto", y luego otra propiedad llamada "Precio" de tipo "Numérico". Una vez hecho esto, el usuario debería poder crear un par de objetos "Bicycle" y completar los valores de las propiedades "Name" y "Price" de cada bicicleta.

Ahora, he visto esta característica en varios productos de software, por lo que debe ser un concepto muy conocido. Mi problema comenzó cuando me senté e intenté crear un esquema de DB para admitir esta estructura de datos, porque quiero que los valores de las propiedades se almacenen utilizando los tipos de columna apropiados. Es decir. un valor de propiedad numérico se almacena como, por ejemplo, una INT en la base de datos, y un valor de propiedad textual se almacena como VARCHAR.

Primero, necesito una tabla que contendrá todos mis definiciones de objetos:

Table obj_defs 

id | name  | 
---------------- 
1 | "Bicycle" | 
2 | "Book" | 

Entonces necesito una mesa para la celebración de qué tipo de propiedades de cada definición de objeto debe tener:

Table prop_defs 

id | obj_def_id | name  | type | 
------------------------------------ 
1 |   1 | "Size" | ? | 
2 |   1 | "Name" | ? | 
3 |   1 | "Price" | ? | 
4 |   2 | "Title" | ? | 
5 |   2 | "Author" | ? | 
6 |   2 | "ISBN" | ? | 

I también necesitaría una tabla que contenga cada objeto:

Table objects 

id | created | updated | 
------------------------------ 
1 | 2011-05-14 | 2011-06-15 | 
2 | 2011-05-14 | 2011-06-15 | 
3 | 2011-05-14 | 2011-06-15 | 

Finalmente, necesito una tabla que ll mantener los valores de las propiedades reales de cada objeto, y una solución es para esta tabla para tener una columna para cada posible tipo de valor, como este:

Table prop_vals 

id | prop_def_id | object_id | numeric | textual | boolean | 
------------------------------------------------------------ 
1 |   1 |   1 |  27 |   |   | 
2 |   2 |   1 |   | "Trek" |   | 
3 |   3 |   1 | 1249 |   |   | 
4 |   1 |   2 |  26 |   |   | 
5 |   2 |   2 |   | "GT" |   | 
6 |   3 |   2 |  159 |   |   | 
7 |   4 |   3 |   | "It" |   | 
8 |   5 |   3 |   | "King" |   | 
9 |   6 |   4 |  9 |   |   | 

Si he implementado este esquema, lo que sería el "tipo" columna de la tabla prop_defs mantener? ¿Enteros que cada mapa a un nombre de columna, varchars que simplemente tienen el nombre de la columna? ¿Alguna otra posibilidad? ¿Me ayudaría un procedimiento almacenado aquí de alguna manera? ¿Y a qué se vería el SQL para obtener la propiedad "nombre" del objeto 2?

Respuesta

28

Está implementando algo llamado Entity-Attribute-Value model http://en.wikipedia.org/wiki/Entity-attribute-value_model.

Mucha gente dirá que es una mala idea (por lo general, soy uno de esos) porque la respuesta a su última pregunta, "¿Qué sería el SQL para buscar ..." tiende a ser "gruesa, peluda y desagradable, y empeorando ".

Estas críticas tienden a mantenerse una vez que permite a los usuarios comenzar a anidar objetos dentro de otros objetos, si no lo permite, la situación seguirá siendo manejable.

Para su primera pregunta, "qué contendría la columna" tipo "de la tabla prop_defs", todo será más simple si tiene una tabla de tipos y descripciones que contiene {"numérico", "Cualquier número"}, {"textual", "String"}, etc. El primer valor es la clave principal. Luego, en prop_defs, su columna "tipo" es una clave externa a esa tabla y contiene valores "numéricos", "textuales", etc. Algunos le dirán incorrectamente que siempre use claves enteras porque se UNEN más rápido, pero si usa los valores " "numérico", "textual", etc. no tiene que INSCRIBIRSE y la unión más rápida es la que usted no hace.

La consulta para tomar un solo valor tendrá una instrucción CASE:

SELECT case when pd.type = "numeric" then pv.numeric 
      when pd.type = "textual" then pv.textual 
      when pd.type = "boolean" then pv.boolean 
    from prov_vals pv 
    JOIN prop_defs pd ON pv.prop_def_id = pv.id 
WHERE pv.object_id = 2 
    AND pd.name = "Name" 
+0

¡Excelente respuesta! Muchas gracias :) –

+2

¿Cuál sería entonces una mejor solución en caso de que el EAV sea algo para evitar cuando se presenta la necesidad de anidar elementos? – ChrisR

+0

Ahora con soluciones NoSQL como MongoDB, los EAV finalmente pueden morir. –

4

Usted debe aceptar que las bases de datos relacionales no son buenos en la prestación de este tipo de funcionalidad. PUEDEN proporcionarlo, pero no son buenos para eso. (Espero estar equivocado). Las bases de datos relacionales se prestan mejor a las interfaces definidas, sin cambiar las interfaces.

--Las tablas de EA dan campos dinámicos pero absorben el rendimiento. Apesta a la indexación. Y es complejo de consultar. Hace el trabajo en muchas situaciones, pero puede desmoronarse en grandes mesas con muchos usuarios accediendo al sistema.

- Las tablas "normales" con varias columnas de marcador de posición son correctas para el rendimiento, pero obtiene nombres de columna no descriptivos y está limitado en el número de columnas que puede "agregar". Además, no es compatible con la separación de subtipos.

- Típicamente usted crea/modifica tablas en tiempo de desarrollo, no tiempo de ejecución. ¿Deberíamos realmente discriminar la modificación de la base de datos en tiempo de ejecución? tal vez tal vez no. La creación de nuevas tablas, claves externas y columnas en tiempo de ejecución puede lograr objetos dinámicos reales, a la vez que proporciona los beneficios de rendimiento de las tablas "regulares". Pero tendría que consultar el esquema de la base de datos y luego generar dinámicamente todas sus consultas. Eso apestaría. Rompería por completo el concepto de tablas como una interfaz.

Cuestiones relacionadas