2010-05-04 9 views
6

En este momento estoy planeando agregar un sistema de filtro a mi sitio.¿Cómo implementar el sistema de filtro en SQL?

Ejemplos:

(ID=apple, COLOR=red, TASTE=sweet, ORIGIN=US) 
(ID=mango, COLOR=yellow, TASTE=sweet, ORIGIN=MEXICO) 
(ID=banana, COLOR=yellow, TASTE=bitter-sweet, ORIGIN=US) 

por lo que ahora estoy interesado en hacer lo siguiente: SELECT ID DE EstaTabla donde el color = 'amarillo' Y SABOR = 'dulce'

Pero mi problema es que soy haciendo esto para múltiples categorías en mi sitio, y las columnas NO son consistentes. (como si la mesa es para teléfonos, entonces será BRAND, 3G-HABILITADO, PRECIO, COLOR, WAVELENGTH, etc.)

¿cómo podría diseñar un esquema general que permita esto?

En este momento estoy pensando en hacer:

table(ID, KEY, VALUE) 

Esto permite que el número arbitrario de columnas, pero para la consulta, estoy usando SELECT ID FROM tabla WHERE (CLAVE = X1 Y VALOR = V1) y (KEY = X2 Y VALOR = V2), .. que devuelve un conjunto vacío.

¿Alguien puede recomendar una buena solución para esto? Tenga en cuenta que el número de columnas CAMBIARÁ regularmente

+1

es para mi sorpresa que Reddit utiliza ampliamente EVA. http://carsonified.com/blog/dev/steve-huffman-on-lessons-learned-at-reddit/ – crapbag

Respuesta

0

Lo que está sugiriendo se conoce como una estructura Entidad-Valor-Atributo y es altamente desaconsejado. Uno de los (muchos) grandes problemas con los diseños de EAV, por ejemplo, es la integridad de los datos. ¿Cómo se hace cumplir que los colores solo consisten en "rojo", "amarillo", "azul", etc.? En resumen, no se puede sin muchos hacks. Otro problema radica en consultar (como has visto) y buscar datos.

En su lugar, recomendaría crear una tabla que represente cada tipo de entidad y, por lo tanto, cada tabla puede tener atributos (columnas) que son específicos de ese tipo de entidad.

Para convertir los datos en columnas en una consulta resultante como lo está buscando, deberá crear lo que a menudo se denomina una consulta cruzada. Hay motores de informes que lo harán y usted puede hacerlo codificar, pero la mayoría de los productos de bases de datos no lo harán de forma nativa (es decir, sin compilar manualmente la cadena SQL). El rendimiento por supuesto no será bueno si tienes muchos datos y te encontrarás con problemas al filtrar los datos. Por ejemplo, supongamos que se supone que algunos de los valores son numéricos. Debido a que es probable que la parte de valor del EAV sea una cadena, tendrá que convertir esos valores en un entero antes de poder filtrarlos y eso supone que los datos serán convertibles en un entero.

7

El modelo entity-attribute-value que sugiere puede encajar en este escenario.

En cuanto a la consulta de filtrado, debe comprender que con el modelo EAV sacrificará un montón de potencia de consulta, por lo que esto puede ser bastante complicado. Sin embargo ésta manera de abordar el problema:

SELECT stuff.id 
FROM  stuff 
JOIN  (SELECT COUNT(*) matches 
      FROM  table 
      WHERE  (`key` = X1 AND `value` = V1) OR 
        (`key` = X2 AND `value` = V2) 
      GROUP BY id 
     ) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id) 
GROUP BY stuff.id; 

Una característica poco elegante de este enfoque es que es necesario especificar el número de pares/valor de atributo que espera igualar en sub_t.matches = 2. Si tuviéramos tres condiciones, tendríamos que especificar sub_t.matches = 3, y así sucesivamente.

Vamos a construir un caso de prueba:

CREATE TABLE stuff (`id` varchar(20), `key` varchar(20), `value` varchar(20)); 

INSERT INTO stuff VALUES ('apple', 'color', 'red'); 
INSERT INTO stuff VALUES ('mango', 'color', 'yellow'); 
INSERT INTO stuff VALUES ('banana', 'color', 'yellow'); 

INSERT INTO stuff VALUES ('apple', 'taste', 'sweet'); 
INSERT INTO stuff VALUES ('mango', 'taste', 'sweet'); 
INSERT INTO stuff VALUES ('banana', 'taste', 'bitter-sweet'); 

INSERT INTO stuff VALUES ('apple', 'origin', 'US'); 
INSERT INTO stuff VALUES ('mango', 'origin', 'MEXICO'); 
INSERT INTO stuff VALUES ('banana', 'origin', 'US'); 

Consulta:

SELECT stuff.id 
FROM  stuff 
JOIN  (SELECT COUNT(*) matches, id 
      FROM  stuff 
      WHERE  (`key` = 'color' AND `value` = 'yellow') OR 
        (`key` = 'taste' AND `value` = 'sweet') 
      GROUP BY id 
     ) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id) 
GROUP BY stuff.id; 

Resultado:

+-------+ 
| id | 
+-------+ 
| mango | 
+-------+ 
1 row in set (0.02 sec) 

Ahora vamos a insertar otra fruta con color=yellow y taste=sweet:

INSERT INTO stuff VALUES ('pear', 'color', 'yellow'); 
INSERT INTO stuff VALUES ('pear', 'taste', 'sweet'); 
INSERT INTO stuff VALUES ('pear', 'origin', 'somewhere'); 

La misma consulta devolvería:

+-------+ 
| id | 
+-------+ 
| mango | 
| pear | 
+-------+ 
2 rows in set (0.00 sec) 

Si queremos restringir este resultado a entidades con origin=MEXICO, tendríamos que añadir otra condición OR y comprobar si en lugar de sub_t.matches = 32.

SELECT stuff.id 
FROM  stuff 
JOIN  (SELECT COUNT(*) matches, id 
      FROM  stuff 
      WHERE  (`key` = 'color' AND `value` = 'yellow') OR 
        (`key` = 'taste' AND `value` = 'sweet') OR 
        (`key` = 'origin' AND `value` = 'MEXICO') 
      GROUP BY id 
     ) sub_t ON (sub_t.matches = 3 AND sub_t.id = stuff.id) 
GROUP BY stuff.id; 

Resultado:

+-------+ 
| id | 
+-------+ 
| mango | 
+-------+ 
1 row in set (0.00 sec) 

Como en cada enfoque, hay ciertas ventajas y desventajas cuando se utiliza el modelo de EAV. Asegúrese de investigar extensamente el tema en el contexto de su aplicación. Es posible que desee considerar una base de datos relacional alternativa, como Cassandra, CouchDB, MongoDB, Voldemort, HBase, SimpleDB u otras tiendas de valores-clave.

+0

Vaya, parece bastante complicado. Gracias por la solución.Hay muchas personas que se oponen a mí usando este diseño, así que por ahora estoy considerando seriamente si debo usar el modelo EVA – crapbag

+0

@sadvaw: La oposición proviene principalmente del hecho de que cuando usas el modelo EAV en una base de datos relacional, es como usar una camioneta para llevarte por la ciudad: por lo tanto, no la estás usando para lo que fue diseñada. Sin embargo, aún se puede hacer, y la viabilidad de esto a menudo depende de la escala (cuánto lo hace o qué tan grande). Por lo tanto, diría que si todo lo que está haciendo en la base de datos es esto, entonces realmente consideraría alternativas a un RDBMS. Sin embargo, si tiene una base de datos más grande y esta es solo una pequeña parte, estas consideraciones podrían ser menos importantes. –

0

El precio que paga por el diseño de mesa simplista en esta etapa le costará en términos de rendimiento a largo plazo. Usar ORM para reducir el costo de modificar la base de datos para que se ajuste a los datos en una estructura adecuada probablemente sea una buena inversión de tiempo, incluso a pesar del costo de rendimiento de ORM.

De lo contrario, es posible que desee buscar un "ORM inverso" que asigne el código de a su base de datos, que tiene la ventaja de ser menos costoso y tener un mayor rendimiento. (Costo de inicio ligeramente más alto en comparación con ORM, pero mejor rendimiento y confiabilidad a largo plazo.)

Es un problema costoso independientemente de cómo lo corte. ¿Desea pagar ahora con el tiempo de desarrollo o pagar más tarde cuando sus tanques de rendimiento? (. "Paga después" es la respuesta incorrecta)

+0

¿Me puede recomendar un diseño de mesa que se adapte a su respuesta? Realmente no entiendo a qué te refieres. – crapbag

+0

Me encontré con el nombre de la teoría a la que aludía: Anchor Modeling. La fuente es un poco académica: http://syslab.dsv.su.se/profiles/blogs/anchor-modeling por lo que puede encontrar esta explicación un poco más fácil de digerir: http://askmonty.org/wiki/Manual: Table_Elimination El punto (relacionado pero separado) de la traducción de bases de datos procesales (ORM o técnicas ORM inversas) es reducir la cantidad de código que tiene que escribir para acceder a una estructura de datos más compleja y especializada que tiene un rendimiento superior, normalización y relacional características. – cbednarski

1

Los siguientes trabajó para mí:

SELECT * FROM mytable t WHERE 
    t.key = "key" AND t.value = "value" OR 
    t.key = "key" AND t.value = "value" OR 
    .... 
    t.key = "key" AND t.value = "value" 
GROUP BY t.id having count(*)=3; 

cuenta (*) = 3 debe coincidir con la cantidad de

t.key = " tecla" Y t.value = "valor"

casos

Cuestiones relacionadas