繁体   English   中英

多对多选择查询

[英]many to many select query

我正在尝试编写代码以从SQL Server数据库中提取产品项列表,并在网页上显示结果。

项目的要求是在页面的右侧显示类别列表作为复选框列表(默认选择所有类别),用户可以取消选中类别并重新查询数据库以仅查看产品他们想要的类别。

继承人开始有点毛茸茸的地方。

可以使用以下产品类别表将每种产品分成多个类别......

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

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

ProductCagetory table

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

我需要选择一个产品列表,这些产品与传递给我的存储过程的一组类别ID相匹配,其中产品具有多个指定的类别。

将类别ID作为逗号分隔的varchar传递给proc,即(3,5,8,12)

SQL将此varchar值分解为临时表中的结果集以进行处理。

我怎么去写这个查询呢?

一个问题是将所选类别的数组或列表传递到服务器中。 Eland Sommarskog 在SQL Server的一系列文章Arrays and Lists中对该主题进行了全面介绍。 将列表作为逗号分隔的字符串传递并构建临时表是一种选择。 还有其他选择,例如使用XML或Table-Valued-Parameter(在SQL Server 2008中)或使用表@variable而不是#temp表。 我所链接的文章涵盖了每种方法的优缺点。

现在就如何检索产品。 首先要做的事情是:如果选择了所有类别,那么使用一个不同的查询,只需检索所有不打扰类别的产品。 这将节省大量性能,并且考虑到所有用户可能首先看到没有未选择任何类别的页面,节省可能很大。

选择的类别,然后构建一个联接产品,类别以及所选类别的查询是相当容易的。 使其扩展和执行是一个不同的主题,完全取决于您的数据模式和所选类别的实际模式。 一个天真的方法是这样的:

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);

ProductsCategoriestable必须在(ProductID, CategoryID)上有一个索引(ProductID, CategoryID)(CategoryID, ProductID)上有一个索引(其中一个是聚簇的,一个是NC)。 这对每个解决方案都是如此。 如果始终选择大多数类别并且结果包含大多数产品,则此查询将起作用。 但是,如果所选类别的列表具有限制性,则最好避免对可能较大的Products表进行扫描,并从所选类别开始:

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;

同样,最佳解决方案在很大程度上取决于数据的形状。 例如,如果您有一个非常偏斜的类别(仅一个类别涵盖了99%的产品),那么最佳解决方案就必须考虑到这种偏差。

这使得所有产品至少处于所有期望的类别(不低于):

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是集合中的类别数。

这将使所有产品完全符合所有期望的类别(不多也不少):

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

双重否定可以理解为:获取没有类别不在所需类别列表中的所有产品。

对于任何所需类别的产品,它很简单:

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)
)

这应该做。 哟不必打破逗号分隔的类别ID。

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)

这将给出任何传递类别ID的产品,即根据JNK的评论,它是OR而不是ALL。 请指定是否需要AND,即只有在逗号分隔列表中指定的所有类别中才需要选择产品。

如果您需要除product_id之外的其他产品,那么您可以编写类似这样的内容(并添加您需要的额外字段):

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);

另一方面,如果您只需要product_id,只需从productcategory_table中选择它们即可:

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

这应该与您正在寻找的相当接近

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

编辑

如评论中所述,这将为出现在多个类别中的那些产品产生重复。 要更正此问题,请在列列表前指定DISTINCT 我在列表product.*包含了所有产品列product.*因为我不知道您要查找哪些列但是您应该将其更改为您想要的特定列

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM