2008-09-24 17 views
61

¿Alguien tiene experiencia en el almacenamiento de pares clave-valor en una base de datos?Pares de valores clave en la base de datos relacional

He estado usando este tipo de tabla:

CREATE TABLE key_value_pairs ( 
    itemid   varchar(32) NOT NULL, 
    itemkey   varchar(32) NOT NULL, 
    itemvalue  varchar(32) NOT NULL, 
    CONSTRAINT ct_primarykey PRIMARY KEY(itemid,itemkey) 
) 

Entonces, por ejemplo, pueden existir las siguientes filas:

itemid   itemkey  itemvalue  
---------------- ------------- ------------ 
123    Colour   Red    
123    Size   Medium    
123    Fabric   Cotton 

El problema con este esquema es la sintaxis SQL necesario para extraer datos es bastante complejo. ¿Sería mejor simplemente crear una serie de columnas clave/valor?

CREATE TABLE key_value_pairs ( 
    itemid   varchar(32) NOT NULL, 
    itemkey1  varchar(32) NOT NULL, 
    itemvalue1  varchar(32) NOT NULL, 
    itemkey2  varchar(32) NOT NULL, 
    itemvalue2  varchar(32) NOT NULL, 
. . .etc . . . 
) 

Esto será más fácil y rápido de consultar pero carece de la extensibilidad del primer enfoque. ¿Algún consejo?

Respuesta

110

Antes de continuar con su enfoque, humildemente le sugiero que retroceda y considere si realmente desea almacenar estos datos en una tabla de "pares clave-valor". No conozco su aplicación pero mi experiencia ha demostrado que cada vez que he hecho lo que está haciendo, más tarde desearía haber creado una tabla de colores, una tabla de tela y una tabla de medidas.

Piense en restricciones de integridad referencial, si se toma el enfoque par clave-valor, la base de datos no se puede decir cuando se está tratando de almacenar un identificador de color en un campo de tamaño

pensar en los beneficios de rendimiento de uniéndose en una tabla con 10 valores frente a un valor genérico que puede tener miles de valores en múltiples dominios. ¿Qué tan útil es realmente un índice sobre Key Value?

Por lo general, el razonamiento detrás de hacer lo que está haciendo es porque los dominios deben ser "definibles por el usuario". Si ese es el caso, incluso yo no te empujaré a crear tablas sobre la marcha (aunque ese es un enfoque viable).

Sin embargo, si su razonamiento es porque cree que será más fácil de administrar que varias tablas, o porque está visualizando una interfaz de usuario de mantenimiento que es genérica para todos los dominios, entonces deténgase y piense detenidamente antes de continuar.

+7

A ++, esto es lo que traté de decir, pero lo articulaste mucho mejor. Tengo varias tablas de pares clave/valor en la base de datos con la que trato y Lamento todos los días. Cada vez que lo hacían porque "ahora necesitamos una solución", y cada vez, sabía que era lo incorrecto. –

+0

¿Hay algún sistema de almacenamiento de datos más allá de SQL que maneje mejor KVP? no hacerlo bien, ¿nadie lo hace bien o no se puede hacer bien? – quillbreaker

+0

@quillbreaker Las soluciones de NoSql giran con frecuencia en torno al almacenamiento eficiente de los pares de kv – mavnn

1

el primer método está bastante bien. puede crear una UDF que extraiga los datos deseados y simplemente llame a eso.

5

Por experiencia, he encontrado que ciertas claves serán más ampliamente utilizadas o consultadas más a menudo. Por lo general, hemos desnormalizado ligeramente el diseño para incluir un campo específico en la tabla principal de "elementos".

por ejemplo. si cada elemento tiene un color, puede agregar la columna Color a su tabla de elementos. La tela y el tamaño se pueden usar con menos frecuencia y se pueden mantener separados en la tabla de pares clave-valor. Incluso puede mantener el color en la tabla de pares clave-valor, pero duplicar los datos en la tabla de elementos para obtener los beneficios de rendimiento.

Obviamente, esto varía según los datos y la flexibilidad que necesita para que los pares clave-valor sean. También puede hacer que los datos de tus atributos no se ubiquen de manera constante. Sin embargo, la desnormalización simplifica en gran medida las consultas y mejora su rendimiento también.

Por lo general, solo consideraría la desnormalización cuando el rendimiento se convierta en un problema, no solo para simplificar una consulta.

0

La segunda tabla está mal des-normalizada. Me quedaría con el primer enfoque.

1

Si tiene muy pocas teclas posibles, entonces simplemente las almacenaría como columnas. Pero si el conjunto de claves posibles es grande, entonces su primer enfoque es bueno (y el segundo enfoque sería imposible).

¿O es así que cada elemento solo puede tener un número finito de claves, pero las claves podrían ser algo de un conjunto grande?

También podría considerar usar un Object Relational Mapper para facilitar las consultas.

+0

El ORM facilita las consultas, pero no mejora el rendimiento. Una consulta SQL codificada a mano podría ofrecer un mejor rendimiento. – mansu

+0

Podría. Pero probablemente no y la velocidad no era algo sobre lo que preguntaba. –

1

El primer método es mucho más flexible a un costo que usted menciona.

Y el segundo enfoque nunca es viable como demostraste. En cambio que haría (como por su primer ejemplo)

create table item_config (item_id int, colour varchar, size varchar, fabric varchar) 

por supuesto esto sólo funcionará cuando se conoce la cantidad de datos y no cambia mucho.

Como regla general, cualquier aplicación que requiera cambiar el DDL de tablas para hacer un trabajo normal debería tener una segunda y tercera consideración.

0

Creo que está haciendo lo correcto, siempre que las claves/valores para un tipo determinado de elemento cambien con frecuencia.
Si son bastante estáticos, entonces simplemente hacer más amplia la tabla de elementos tiene más sentido.

Utilizamos un enfoque similar (pero bastante más complejo), con mucha lógica alrededor de las claves/valores, así como tablas para los tipos de valores permitidos para cada clave.
Esto nos permite definir elementos como solo otra instancia de una clave, y nuestra tabla central asigna tipos de claves arbitrarias a otros tipos de claves arbitrarias. Puede atar tu cerebro rápidamente en nudos, pero una vez que hayas escrito y encapsulado la lógica para manejarlo todo, tienes mucha flexibilidad.

Puedo escribir más detalles de lo que hacemos si es necesario.

2

No entiendo por qué el SQL para extraer datos debe ser complejo para su primer diseño. Sin duda, para obtener todos los valores de un elemento, que acaba de hacer esto:

SELECT itemkey,itemvalue FROM key_value_pairs WHERE itemid='123'; 

o si sólo quiere tener una clave particular para ese artículo:

SELECT itemvalue FROM key_value_pairs WHERE itemid='123' AND itemkey='Fabric'; 

El primer diseño también le da la flexibilidad para facilidad agregue nuevas llaves cuando lo desee.

+0

se complica si uno de los valores es una fecha y desea buscar entre fechas para ciertas claves. –

+3

Piense en la consulta a la inversa: encuentre el itemid para un conjunto de pares clave/valor, esto requiere un conjunto de uniones en cascada. Se complica aún más por la necesidad de evitar seleccionar un superconjunto; p.ej. find (Color = Rojo, Tamaño = Medio) no devuelve itemid 123 ya que ese conjunto contiene otra fila (Fabric = Cotton) – horace

0

Si las claves son dinámicas, o hay muchas, utilice la tabla de asignación que tiene como primer ejemplo. Además, esta es la solución más general, se adapta mejor en el futuro a medida que agrega más claves, es fácil codificar el SQL para obtener los datos, y la base de datos podrá optimizar la consulta mejor de lo que imagina (es decir, no me esforzaría en optimizar prematuramente este caso a menos que se pruebe que es un cuello de botella en las pruebas posteriores, en cuyo caso podría considerar las siguientes dos opciones a continuación).

Si las claves son un conjunto conocido, y no hay muchas (< 10, quizás < 5), no veo el problema de tenerlas como columnas de valores en el elemento.

Si hay un número medio de teclas fijas conocidas (10 - 30), entonces tal vez tenga otra tabla para contener los detalles del elemento.

Sin embargo, nunca veo la necesidad de utilizar su segunda estructura de ejemplo, parece engorroso.

16

Hay otra solución que se encuentra en algún lugar entre los dos. Puede usar una columna de tipo xml para las claves y valores. Así que mantienes el campo itemid, luego tienes un campo xml que contiene el xml definido para algunos pares de valores clave como <items> <item key="colour" value="red"/><item key="xxx" value="blah"/></items> Luego, cuando extraes tus datos de la base de datos, puedes procesar el xml de diferentes maneras. Dependiendo de tu uso Esta es una solución extensible.

+0

Ese es un posible escenario Yo preferiría el concepto de KV simple también. Separa los datos (clave, valor) de sus metadatos (por ejemplo, en una configuración XML en una columna seprada de "propiedades"). Flexible, extensible y fácil de procesar (por ejemplo, JAXB). No tiene que cambiar el esquema DB todo el tiempo cuando amplía su lógica comercial. La lógica de persistencia (carga/guardado) y la interfaz con el dominio se pueden desarrollar una vez usando "Convención sobre configuración" y no es necesario tocar para cambios/extensiones. –

1

La violación de las reglas de normalización está bien siempre que el requisito comercial aún pueda cumplirse. Tener key_1, value_1, key_2, value_2, ... key_n, value_n puede ser correcto, hasta el punto en que necesite key_n+1, value_n+1.

Mi solución ha sido una tabla de datos para atributos compartidos y XML para atributos únicos. Eso significa que uso ambos. Si todo (o la mayoría de las cosas) tiene un tamaño, entonces el tamaño es una columna en la tabla. Si solo el objeto A tiene el atributo Z, entonces Z se almacena como XML, como la respuesta de Peter Marshall ya dada.

+0

La violación de las reglas de normalización NO está bien, siempre y cuando los requisitos de negocios aún puedan cumplirse. Violar las reglas de normalización está bien, siempre y cuando los datos normalizados no sean efectivos, incluso entonces, realmente querría una versión normalizada de los datos y una vista materializada normalizada. –

+0

La solución key_n, value_n hace que SQl sea realmente difícil. ¿Cómo codificaría la sal para "fabric = 'cotton' y color = 'Red'? Usted terminaría con: donde (key_1 =" Fabric "y value_1 =" Cotton " o key_2 =" Fabric "y value_1 = "Cotton" .... y (... –

13

En la mayoría de los casos en que usaría el primer método, es porque realmente no se ha sentado y pensado en su modelo ... "Bueno, todavía no sabemos cuáles serán las llaves". En general, este es un diseño bastante pobre. Va a ser más lento que tener tus llaves como columnas, lo cual debería ser.

También me pregunto por qué su identificación es varchar.

En el raro caso de que realmente deba implementar una tabla de clave/valor, la primera solución está bien, aunque, en general, quisiera tener las claves en una tabla separada para no almacenar varchars como claves en tu tabla de clave/valor.

por ejemplo,

CREATE TABLE valid_keys ( 
    id   NUMBER(10) NOT NULL, 
    description varchar(32) NOT NULL, 
    CONSTRAINT pk_valid_keys PRIMARY KEY(id) 
); 

CREATE TABLE item_values ( 
    item_id NUMBER(10) NOT NULL, 
    key_id NUMBER(10) NOT NULL, 
    item_value VARCHAR2(32) NOT NULL, 
    CONSTRAINT pk_item_values PRIMARY KEY(id), 
    CONSTRAINT fk_item_values_iv FOREIGN KEY (key_id) REFERENCES valid_keys (id) 
); 

A continuación, puede incluso ir frutos secos y añadir un "tipo" de las teclas, lo que permite un poco de comprobación de tipos.

0

Si va por la ruta de una tabla KVP, y tengo que decir que no me gusta esa técnica en absoluto, ya que es realmente difícil de consultar, debe considerar agrupar los valores para una sola ID de elemento juntos usando una técnica apropiada para cualquier plataforma en la que estés.

RDBMS tienen una tendencia a dispersar filas para evitar la contención de bloque en las inserciones y si tiene 8 renglones para recuperar puede encontrar fácilmente accediendo a 8 bloques de la tabla para leerlos. En Oracle haría bien en considerar un clúster de hash para almacenarlos, lo que mejoraría enormemente el rendimiento al acceder a los valores para una identificación de artículo determinada.

-1

Su ejemplo no es un muy buen ejemplo del uso de pares de valores clave. Un mejor ejemplo sería el uso de algo así como una tabla de tarifas, una tabla de clientes y una tabla de sugerencias de clientes en una aplicación de facturación.La Tabla de cuotas consistiría en campos como: fee_id, FEE_NAME, fee_description la tabla Customer_Fee consistiría en campos como: customer_id, fee_id, fee_value

2

Creo que la mejor manera de diseñar este tipo de tablas es el siguiente:

  • Convierta los campos utilizados con frecuencia como columnas en la base de datos.
  • Proporcione una columna Misc que contenga un diccionario (en JSON/XML/otra cadena formeat) que contendrá los campos como pares clave-valor.

puntos salientes:

  • Usted puede escribir sus consultas SQL normales para consultar SQL en la mayoría de las situaciones.
  • Puede hacer un FullTextSearch en los pares clave-valor. MySQL tiene un motor de búsqueda de texto completo, de lo contrario puede usar consultas "me gusta" que son un poco más lentas. Si bien la búsqueda de texto completo es mala, suponemos que esas consultas son menos, por lo que no deberían causar demasiados problemas.
  • Si sus pares clave-valor son indicadores booleanos simples, esta técnica tiene el mismo poder que tener una columna separada para la clave. Cualquier operación más compleja en los pares de valores clave debe realizarse fuera de la base de datos.
  • Si observa la frecuencia de las consultas durante un período de tiempo, le indicará qué pares clave-valor deben convertirse en columnas.
  • Esta técnica también hace que sea fácil forzar restricciones de integridad en la base de datos.
  • Proporciona una ruta más natural para que los desarrolladores vuelvan a factorizar su esquema y código.
12

Una vez usé pares clave-valor en una base de datos con el propósito de crear una hoja de cálculo (utilizada para el ingreso de datos) en la que un cajero resumiera su actividad de trabajar en un cajón de efectivo. Cada par de k/v representaba una celda nombrada en la que el usuario ingresaba una cantidad monetaria. La razón principal de este enfoque es que la hoja de cálculo estaba muy sujeta a cambios. Se agregaron nuevos productos y servicios de forma rutinaria (así aparecieron nuevas células). Además, ciertas células no eran necesarias en ciertas situaciones y podían descartarse.

La aplicación que escribí fue una reescritura de una aplicación que dividió la hoja de la caja en secciones separadas, cada una representada en una tabla diferente. El problema aquí era que a medida que se agregaban productos y servicios, se requerían modificaciones de esquema. Al igual que con todas las opciones de diseño, existen ventajas y desventajas para tomar una determinada dirección en comparación con otra. Mi rediseño sin duda se hizo más lento y consumió más rápidamente el espacio en disco; sin embargo, fue muy ágil y permitió agregar nuevos productos y servicios en minutos. Sin embargo, la única cuestión que se debe tener en cuenta es el consumo de disco; no había otros dolores de cabeza que pueda recordar.

Como ya mencioné, la razón por la que generalmente considero un enfoque de pares clave-valor es cuando los usuarios -este podría ser el propietario de la empresa- desean crear sus propios tipos con un conjunto de atributos específico del usuario. En tales situaciones, he llegado a la siguiente determinación.

Si no hay necesidad de recuperar datos por estos atributos o la búsqueda se puede diferir a la aplicación una vez que se ha recuperado un trozo de datos, recomiendo almacenar todos los atributos en un solo campo de texto (usando JSON, YAML, XML, etc.). Si hay una gran necesidad de recuperar datos por estos atributos, se vuelve complicado.

Puede crear una sola tabla de "atributos" (id, item_id, key, value, data_type, sort_value) donde la columna de ordenamiento codifica el valor real en una representación de cadena ordenable. (por ejemplo, fecha: "2010-12-25 12:00:00", número: "0000000001") O puede crear tablas de atributos separadas por tipo de datos (por ejemplo, atributos_cadena, atributos_de_data, atributos_de_úmero). Entre los numerosos pros y contras de ambos enfoques: el primero es más simple, el segundo es más rápido. Ambos causarán que escriba consultas complejas y desagradables.

0

Los tiempos han cambiado. Ahora tiene otros tipos de bases de datos que puede usar al lado de las bases de datos relacionales. Las opciones de NOSQL ahora incluyen, Almacenes de columna, Almacenes de documentos, Gráfico y Multi-modelo (Ver: http://en.wikipedia.org/wiki/NoSQL).

Para las bases de datos de valores-clave, sus opciones incluyen (pero no se limitan a) CouchDb, Redis y MongoDB.

1

PostgreSQL 8.4 admite el tipo de datos hstore para almacenar conjuntos de pares (clave, valor) dentro de un único campo de datos PostgreSQL. Consulte http://www.postgresql.org/docs/8.4/static/hstore.html para obtener información sobre su uso. Aunque es una pregunta muy antigua, pero pensé pasar esta información pensando que podría ayudar a alguien.

Cuestiones relacionadas