2010-02-15 25 views
5

Soy bastante principiante y tengo dos tablas: "producto" y "atributos del producto".Seleccionar contra subconjuntos de una lista en MySQL

He aquí algunos datos imaginarios (las cosas reales implica más tablas)

los productos de mesa: Atributos

product_id | product_name     
10   | aaa       
11   | bbb 
12   | ccc 

Producto Tabla:

attribute_id | product_id 
     21  | 10   
     23  | 10   
     24  | 10   
     21  | 11   
     24  | 11   
     21  | 12   
     25  | 12   

Donde cada producto tiene más de un atributo posible . Tengo una lista de identificadores de atributos como (21,10,25) y debo seleccionar todos los productos cuyos atributos sean un subconjunto de esa lista.

¿Es posible hacer esto en una consulta?

Cuando filtro para (21,24) de salida deseado es volver único producto 11 (bbb)

Cuando filtro para (21,23,24) de salida deseada es de devolver los productos 10 y 11.

Cuando filtro para (21) la salida deseada es para devolver ninguno (porque todos los productos tienen al menos otro atributo).

Respuesta

5

Si usted pretende que el filtro está en una tabla:

select * 
from product p 
where not exists (
    select 1 
    from attributes a 
    where a.product_id = p.product_id 
    and not exists(
     select 1 
     from filter f 
     where f.id_attribute = a.id_attribute)) 

Si fue construida en una consulta:

select * 
from product p 
where not exists (
    select 1 
    from attributes a 
    where a.product_id = p.product_id 
    and attribute_id not in (<list>)) 

Esto está fuera de mi cabeza, por lo que pueden tener errores tipográficos.

+1

sí, el segundo funciona, gracias –

+0

Funciona gracias :) – Ashu

1

Asumiendo que su tabla de productos se llama producto y la columna ID de esa tabla se acaba de llamar Id:

SELECT * from Product p where p.Id IN 
    (Select id_product from ProductAttributes where id_attribute in (21, 23, 24)) 
+0

sí, pero eso también seleccionaría productos que tienen un atributo en la lista y el otro no en la lista. Ya lo hice así.Necesito que tengan todos los atributos en la lista (como dije), así que cada posible fila de la tabla de atributos con esa product_id –

1
select 
    P.id, 
    P.name, 
    count(P.id) as matched_attr_count, 
    count(PA.a_id) as total_attr_count 
from 
    product_attributes PA 
    left join product P on P.id = PA.p_id and PA.a_id in (21,23,24) 
group by 
    PA.p_id 
having 
    matched_attr_count = total_attr_count; 
+0

esta solución también parece funcionar, excepto que devuelve más filas por producto, por lo que sería beneficioso para un grupo por P. id_product en él editar: no, no funciona .. mismo problema, no filtra todos los atributos de un producto, es suficiente para que uno de ellos esté en la lista –

+0

sí, pero también está el otro problema que no filtra todos los atributos –

+0

Te malentendí primero. Parece arreglado ahora? – Qwerty

-1

me deja publicar datos imaginarios simples (las cosas reales implica más tablas)

mesa productos

product_id | product_name     
10   | aaa       
11  | bbb 

mesa product_attribute

attribute_id | product_id <br> 
21  | 10   
23  | 10   
24  | 10   
21  | 11   
24  | 11 

quiero que:

  • cuando filtro para (21,24) a ser devuelto único producto 11 (bbb)
  • cuando filtro para (21,23,24) a devolver tanto productos
  • cuando filtro para (21) solo para devolver ninguno (porque no pro conducto sólo tiene ese atributo)
+0

esto está en prestashop, si necesita un módulo para filtrar por atributos , pero en tablas y relaciones más complejas –

+1

, le sugiero que edite la pregunta y la agregue a la pregunta. –

+1

Este tipo de información debe incluirse en su pregunta original. Es posible editar su pregunta si luego desea incluir más información. He editado tu pregunta por ti. –

1

Esto debería devolver sólo aquellos de id donde todos los atributos para cada ID están contenidas completamente dentro de la lista:

select attribute_match.id_product from 
(select id_product, count(*) c from attributes 
    where id_attribute in (21, 10, 25) 
    group by id_product) attribute_match, 
(select id_product, count(*) c_count from attributes 
    group by id_product) attribute_total 
where attribute_match.id_product = attribute_total.id_product 
     and attribute_match.c = attribute_total.c 
+0

esto no funciona en el caso (21,23,24) (selecciona solo 1 producto) –

+0

Creo que la consulta revisada debería funcionar: la primera selección interna debería generar 10,3 11,2 y la segunda selección interna debería generar 10 , 3 11,2 y por lo tanto la selección externa debe coincidir tanto con 10 como con 11. – user273224

+0

sí, ahora funciona, se publicó una forma más fácil pero aún se obtiene un voto –

1

Hasta MySQL soporta la combinación EXCEPT consulta,

SELECT product_id 
    FROM attributes 
    WHERE product_id NOT IN (
     SELECT product_id 
     FROM attributes 
     WHERE attribute_id NOT IN (21, 23, 24) 
    ) 
    GROUP BY product_id 
UNION 
SELECT id 
    FROM products AS p 
    LEFT JOIN attributes AS a 
    ON p.id = a.product_id 
    WHERE a.product_id IS NULL 

Si usted desea tener sólo los productos con todos los atributos dados, añadir una cláusula HAVING COUNT(*)=n a la primera consulta externa, donde 'n' es la longitud de la lista de atributos.

+0

mismo, no funciona en el caso (21,23,24) según mi ejemplo –

+0

@Bogdan: el código original era para revisión de la pregunta 3. Actualización para que coincida con la revisión actual (7). – outis

+0

No modifiqué la pregunta, solo la expliqué mejor con ejemplos. Gracias por el esfuerzo de todas formas, obtuviste un voto mío –

0

Sobre la base de sus chicos Insight, que optimiza aún más y se utilizará únicamente 1 comunicado RECUENTO así:

SELECT * ,COUNT(p.product_id) AS c FROM product_attribute pa 
LEFT JOIN products p ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter_list) 
GROUP BY pa.product_id 
HAVING c=0 

¿Funciona? :)

Editar:
Ese código no devuelve el nombre del producto u otros campos que pueda tener. Esta es la correcta:

SELECT * ,COUNT(pa.product_id) AS c FROM products p 
LEFT JOIN product_attribute pa ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter) 
GROUP BY p.product_id 
HAVING c=0