繁体   English   中英

MySQL构造此查询的最佳方法?

[英]MySQL best way to construct this query?

我继承了一个数据库,该数据库的结构包含一个产品表,一个由某些产品属性组成的表以及另一个用于在这些属性与给定产品之间建立关系的表。

用户可以通过这些属性的组合来过滤产品,这意味着如果选择了多个属性,则仅返回具有所有这些属性的产品。 不幸的是,该规则现在有一个例外,即选择两个特定属性之一的用户需要包含其中一个(或两个)的结果。

查询当前看起来像这样(不是我的代码):

SELECT DISTINCT p.* FROM products AS p 
INNER JOIN attributes a ON p.product_id=a.property_id 
WHERE a.attribute_id IN (1,3,7) 
GROUP BY p.property_id 
HAVING COUNT(DISTINCT a.attribute_id) = 3 

我怀疑以上内容是检索所需产品的特别有效的方法,但是我不确定如何根据新要求进行操作。

现在,当选择了两个“特殊”属性(3和7)时,我已经创建了一些php代码来构造一个特殊的查询:

SELECT DISTINCT p.* FROM products AS p 
INNER JOIN attributes a ON p.product_id=a.property_id 
WHERE a.attribute_id IN (1,3) OR a.attribute_id IN (1,7) 
GROUP BY p.property_id 
HAVING COUNT(DISTINCT a.attribute_id) = 2

但是,这仍然无法按要求工作-共享这两个属性的任何产品都不会返回结果中(这显然是由于HAVING COUNT子句引起的,但我不知道如何解决它。为了清楚起见,问题是,如果10个产品仅具有属性3,而另外五个具有属性3和7,则上述查询将仅返回10条记录。

可能可以使用某种子查询,或者有哪些替代方法?

该查询看起来不错,但是您可以删除DISTINCT修饰符,因为您已经按ID进行了分组。 关于新要求,您能否在到达SQL查询之前在代码中解决它?

编辑:一种替代方法是使用每个所需属性的一个内部联接构造查询,但这可能会慢得多

我认为看起来已经不错。 除了强制性的“不要选择*”外,对我来说还可以。

我的建议:如果它可以正常工作并且不引起性能问题,请将其留在其他地方。 如果将来出现问题,请重新访问。

这是执行原始查询的更好方法:

SELECT ... FROM products AS p 
INNER JOIN attributes a1 ON p.product_id=a1.property_id AND a1.attribute_id=1
INNER JOIN attributes a2 ON p.product_id=a2.property_id AND a2.attribute_id=3
INNER JOIN attributes a3 ON p.product_id=a3.property_id AND a3.attribute_id=7

并且,假设您需要attribute_id 3,则需要将其与attribute_id 7进行“或”运算,并假设还需要attribute_id 1,这不是那些特殊属性之一:

SELECT ... FROM products AS p 
INNER JOIN attributes a1 ON p.product_id=a1.property_id AND a1.attribute_id=1
LEFT OUTER JOIN attributes a2 ON p.product_id=a2.property_id AND a2.attribute_id=3
LEFT OUTER JOIN attributes a3 ON p.product_id=a3.property_id AND a3.attribute_id=7
WHERE a2.attribute_id IS NOT NULL OR a3.attribute_id IS NOT NULL

我怀疑这些方法中的任何一个都将比具有单独/具有/按汇总操作分组的原始方法快得多。 属性表应该在(property_id,attribute_id)或(attribute_id,property_id)上都具有多列唯一索引,尽管我认为property_id具有更高的选择性,因此应该是索引中最左边的列。

有了适当的(显而易见的)索引,在MySQL中这将非常有效。

选择 ...

从产品AS p

INNER JOIN属性a1 ON p.product_id = a1.property_id AND a1.attribute_id = 1
左联接属性a2 ON p.product_id = a2.property_id和a2.attribute_id = 3
左联接属性a3 ON p.product_id = a3.property_id和a3.attribute_id = 7

在哪里(
当a1.product_attribute_id为NULL则为0,否则为1结束
+当a1.product_attribute_id为NULL则为0,否则为1结束
)> 0

如何选择p。*并仅按1列分组? 还是可以使用主键?

其中a1.attribute_id IN(1,3)或a1.attribute_id IN(1,7)

是相同的

在哪里a1.attribute_id IN(1、3、7)

SELECT p.* FROM products  
INNER JOIN (
    SELECT a1.property_id  
    FROM attributes a1 
    WHERE a1.attribute_id IN (1,3,7)
    GROUP BY a1.property_id 
    HAVING COUNT(DISTINCT a1.attribute_id) = 2
) as a ON p.product_id=a.property_id

暂无
暂无

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

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