2010-09-21 35 views
5

Estoy tratando de escribir código para obtener una lista de productos de una base de datos de SQL Server y mostrar los resultados en una página web.muchos a muchos seleccione consulta

Un requisito del proyecto es que una lista de categorías se muestre en el lado derecho de la página como una lista de casillas de verificación (todas las categorías seleccionadas por defecto) y un usuario puede desmarcar categorías y volver a consultar la base de datos para ver productos en solo las categorías que ellos quieren.

Aquí es donde empieza a ponerse un poco peludo.

Cada producto se puede assinged a múltiples categorías utilizando una tabla de categorías de productos como a continuación ...

 
Product table 
[product_id](PK),[product_name],[product_price],[isEnabled],etc... 

Category table 
[CategoryID](PK),[CategoryName] 

ProductCagetory table 

[id](PK),[CategoryID](FK),[ProductID](FK) 

necesito para seleccionar una lista de productos que responden a un conjunto de ID de la categoría del pasado a mi procedimiento almacenado donde los productos tienen múltiples categorías asignadas.

el ID categort de se pasan a la proc como una coma delimitado varchar es decir (3,5,8,12)

Las roturas SQL este valor varchar en un conjunto de resultados en una tabla temporal para su procesamiento.

¿Cómo voy a escribir esta pregunta?

+1

¿Quieres este exclusivo o inclusivo? Por ejemplo, si selecciono solo las categorías 1 y 2, ¿desea ver todas las visitas en 1 o 2, o solo esos elementos en AMBOS 1 Y 2? – JNK

+0

Por favor, publique lo que tiene hasta ahora. –

Respuesta

-2

Esto debe estar bastante cerca de lo que busca

SELECT product.* 
FROM product 
JOIN ProductCategory ON ProductCategory.ProductID = Product.product_id 
JOIN #my_temp ON #my_temp.category_id = ProductCategory.CategoryID 

EDITAR

Como se señaló en los comentarios esto producirá duplicados de dichos productos que aparecen en varias categorías. Para corregir esto, especifique DISTINCT antes de la lista de columnas. He incluido todas las columnas de productos en la lista product.*, ya que no sé qué columnas está buscando, pero probablemente debería cambiarlas a las columnas específicas que desee

+3

Si un producto pertenece a múltiples categorías, esta consulta generará duplicados. –

+0

Saludos Steve, trabajó un regalo –

+0

@Remus. De hecho lo hace.Editado para reflejar que –

-1

Esto debería hacer. No es necesario que rompas los identificadores de categoría delimitados por comas.

select distinct p.* 
from product p, productcategory pc 
where p.product_id = pc.productid 
and pc.categoryid in (place your comma delimited category ids here) 

Esto dará a los productos que se encuentran en cualquiera de los identificadores de pasada en la categoría, es decir, de acuerdo con el comentario de JNK su un OR no todos. Especifique si desea un AND, es decir, el producto debe seleccionarse solo si se encuentra en TODAS las categorías especificadas en la lista separada por comas.

+2

El problema de ubicar lo distinto tan alto en el árbol de ejecución es que tendrá que clasificar potencialmente muchos productos para obtener los 'distict' y este tipo es (muy) costoso. Es mejor obtener los identificadores de productos distintos primero (es decir, ordenar solo los ID) y luego obtener el resto de la información del producto. –

+0

Usando DISTINCT como una curita para la necesidad de una semi-unión: no tan genial. – ErikE

-1

Si necesita algo más que product_id de productos, entonces puede escribir algo como esto (y la adición de los campos extra que necesita):

SELECT distinct(p.product_id) 
FROM product_table p 
JOIN productcategory_table pc 
ON p.product_id=pc.product_id 
WHERE pc.category_id in (3,5,8,12); 

por el contrario si necesita realmente sólo la product_id simplemente puede seleccionarlos de productcategory_table:

SELECT distinct(product_id) 
FROM productcategory_table 
WHERE category_id in (3,5,8,12); 
+1

Igual que la consulta de Steve, esto produce duplicados para productos en múltiples categorías. –

+0

su derecho fijo, thx –

+0

Usando DISTINCT como una curita sobre la necesidad de una semi-unión: no tan genial. – ErikE

2

Uno el problema es pasar la matriz o la lista de categorías seleccionadas al servidor. El tema fue cubierto en general por Eland Sommarskog en la serie de artículos Arrays and Lists in SQL Server. Pasar la lista como una cadena separada por comas y construir una tabla temporal es una opción. Hay alternativas, como usar XML, o un parámetro de tabla-valorado (en SQL Server 2008) o usar una tabla @variable en lugar de una tabla #temp. Los pros y los contras de cada uno están cubiertos en los artículos que he vinculado.

Ahora sobre cómo recuperar los productos. Lo primero es lo primero: si se seleccionan todas las categorías, entonces use una consulta diferente que simplemente recupere todos los productos sin preocuparse por las categorías. Esto ahorrará un lote de rendimiento y considerando que todos los usuarios probablemente verán primero una página sin seleccionar ninguna categoría, el ahorro puede ser significativo.

Cuando las categorías son seleccionadas, la creación de una consulta que una los productos, categorías y categorías seleccionadas es bastante fácil. Hacerlo escalar y realizar es un tema diferente, y depende por completo de su esquema de datos y del patrón real de las categorías seleccionadas. Un enfoque ingenuo es como este:

select ... 
from Products p 
where p.IsEnabled = 1 
and exists (
    select 1 
    from ProductCategories pc 
    join #selectedCategories sc on sc.CategoryID = pc.CategoryID 
    where pc.ProductID = p.ProductID); 

El ProductsCategoriestable debe tener un índice en (ProductID, CategoryID) y uno en (CategoryID, ProductID) (uno de ellos es el agrupado, uno es NC). Esto es cierto para cada solución por cierto. Esta consulta funcionaría si la mayoría de las categorías siempre se seleccionan y el resultado contiene más productos de todos modos. Pero si la lista de categorías seleccionadas es restrictiva a continuación, es mejor evitar la exploración en la tabla productos potencialmente grande y comenzar desde las categorías seleccionadas:

with distinctProducts as (
select distinct pc.ProductID 
from ProductCategories pc 
join #selectedCategories sc on pc.CategoryID = sc.CategoryID) 
select p.* 
from Products p 
join distinctProducts dc on p.ProductID = dc.ProductID; 

Una vez más, la mejor solución depende en gran medida de la forma de sus datos. Por ejemplo, si tiene una categoría muy sesgada (un categoru solo cubre el 99% de los productos), entonces la mejor solución debería tener en cuenta este sesgo.

+0

BTW Supuse a partir de la explicación que el resultado debe coincidir con * any * categoría (al menos uno). Como otros han señalado, es un problema diferente si el producto tiene que coincidir con * todas * categorías. –

1

Esto se lleva todos los productos que son al menos en todas las categorías deseadas (no menos):

select * from product p1 join (
    select p.product_id from product p 
    join ProductCategory pc on pc.product_id = p.product_id 
    where pc.category_id in (3,5,8,12) 
    group by p.product_id having count(p.product_id) = 4 
) p2 on p1.product_id = p2.product_id 

4 es el número de categorías en el conjunto.

Esto se lleva todos los productos que son exactamente en todas las categorías deseadas (ni más ni menos):

select * from product p1 join (
    select product_id from product p1 
    where not exists (
    select * from product p2 
    join ProductCategory pc on pc.product_id = p2.product_id 
    where p1.product_id = p2.product_id 
    and pc.category_id not in (3,5,8,12) 
) 
    group by product_id having count(product_id) = 4 
) p2 on p1.product_id = p2.product_id 

La doble negación se puede leer como: obtener todos los productos para los que existen no categorías que son no en la lista de categorías deseadas.

Para los productos en cualquier de las categorías deseadas, es tan simple como:

select * from product p1 where exists (
    select * from product p2 
    join ProductCategory pc on pc.product_id = p2.product_id 
    where 
    p1.product_id = p2.product_id and 
    pc.category_id in (3,5,8,12) 
) 
+0

No me gusta el uso de IN cuando lo que realmente quieres decir es un JOIN. Claro, el optimizador es lo suficientemente inteligente como para convertirlo en un JOIN * en * casos * en lugar de expandirlo a una cláusula OR, pero confiar en eso es, bueno, un atajo poco confiable, casi un hackeo. – ErikE

+0

@Emtucifor: ¡Estoy de acuerdo! Como las consultas internas son importantes para el ejemplo, no pensé mucho en las externas. Corregido eso ahora. La última consulta usa un "join" existente para que no genere filas duplicadas. –

Cuestiones relacionadas