简体   繁体   English

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

[英]MySQL best way to construct this query?

I've inherited a database that has a structure with a table of products, a table consisting of some product attributes and another table to build the relationship between these attributes and a given product. 我继承了一个数据库,该数据库的结构包含一个产品表,一个由某些产品属性组成的表以及另一个用于在这些属性与给定产品之间建立关系的表。

A user can filter the products by a combination of these attributes, meaning that if more than one attribute is selected only products with all those attributes are returned. 用户可以通过这些属性的组合来过滤产品,这意味着如果选择了多个属性,则仅返回具有所有这些属性的产品。 Unfortunately, there is now an exception to this rule, whereby a user selecting one of two specific attributes needs results containing either (or both). 不幸的是,该规则现在有一个例外,即选择两个特定属性之一的用户需要包含其中一个(或两个)的结果。

The query currently looks like this (not my code): 查询当前看起来像这样(不是我的代码):

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 

I doubt the above is a particularly efficient way of retrieving the required products, but I'm unsure how to proceed in light of the new requirement. 我怀疑以上内容是检索所需产品的特别有效的方法,但是我不确定如何根据新要求进行操作。

I've now created some php code to construct a special query when the two "special" attributes (3 and 7) are selected: 现在,当选择了两个“特殊”属性(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

However, this still does not work as required - any products that share both these attributes are not returned in the result (this is obviously due to the HAVING COUNT clause, but I don't know how I go about fixing it. For clarity, the issue is if 10 products have only attribute 3 but a further five have attributes 3 and 7, the above query will only return the 10 records. 但是,这仍然无法按要求工作-共享这两个属性的任何产品都不会返回结果中(这显然是由于HAVING COUNT子句引起的,但我不知道如何解决它。为了清楚起见,问题是,如果10个产品仅具有属性3,而另外五个具有属性3和7,则上述查询将仅返回10条记录。

Might it be possible to use some kind of subquery or what alternatives are there? 可能可以使用某种子查询,或者有哪些替代方法?

The query seems fine, except that you possibly could remove the DISTINCT modifier, since you're already grouping by id. 该查询看起来不错,但是您可以删除DISTINCT修饰符,因为您已经按ID进行了分组。 Regarding the new requirement, can't you solve it in your code, before it reaches the SQL query? 关于新要求,您能否在到达SQL查询之前在代码中解决它?

Edit: An alternative would be to construct the query with one inner join for each required attribute, but that would probably be much slower 编辑:一种替代方法是使用每个所需属性的一个内部联接构造查询,但这可能会慢得多

I think that looks pretty good already. 我认为看起来已经不错。 Aside from the obligatory mention of "don't do select *", it looks ok to me. 除了强制性的“不要选择*”外,对我来说还可以。

My advice: if it works and its not causing a performance issue, leave it and spend your time on something else. 我的建议:如果它可以正常工作并且不引起性能问题,请将其留在其他地方。 Revisit it in future if an issue arises. 如果将来出现问题,请重新访问。

This the better way to do the original query: 这是执行原始查询的更好方法:

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

And, given that if you need attribute_id 3, you want to OR that with attribute_id 7, and assuming you also want attribute_id 1, which is not one of those special attributes: 并且,假设您需要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

I suspect either of these will be much faster than the originals with the distinct/having/group by aggregate operations. 我怀疑这些方法中的任何一个都将比具有单独/具有/按汇总操作分组的原始方法快得多。 The attributes table should have a multicolumn unique index on either (property_id, attribute_id) or (attribute_id, property_id), though I assume property_id is more selective and therefore should be the leftmost column in the index. 属性表应该在(property_id,attribute_id)或(attribute_id,property_id)上都具有多列唯一索引,尽管我认为property_id具有更高的选择性,因此应该是索引中最左边的列。

With proper (and obvious) indexes, this will be very efficient in MySQL. 有了适当的(显而易见的)索引,在MySQL中这将非常有效。

SELECT ... 选择 ...

FROM products AS p 从产品AS p

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

WHERE ( 在哪里(
CASE WHEN a1.product_attribute_id IS NULL THEN 0 ELSE 1 END 当a1.product_attribute_id为NULL则为0,否则为1结束
+ CASE WHEN a1.product_attribute_id IS NULL THEN 0 ELSE 1 END +当a1.product_attribute_id为NULL则为0,否则为1结束
) > 0 )> 0

how can you select p.* and group by only 1 column? 如何选择p。*并仅按1列分组? Or does that work with a primary key? 还是可以使用主键?

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

is the same as 是相同的

WHERE a1.attribute_id IN (1,3,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