MySQL构建此查询的最佳方法是什么?

时间:2008-12-06 16:26:10

标签: mysql database

我继承了一个数据库,该数据库具有包含产品表的结构,包含一些产品属性的表和另一个用于构建这些属性与给定产品之间关系的表。

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

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

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 

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

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

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但另外5个产品具有属性3和7,则上述查询将仅返回10个记录。

是否有可能使用某种子查询或有哪些替代方案?

5 个答案:

答案 0 :(得分:2)

查询似乎很好,除了您可能删除DISTINCT修饰符,因为您已经按ID分组。关于新的需求,在到达SQL查询之前,你不能在代码中解决它吗?

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

答案 1 :(得分:1)

我认为这看起来很不错。除了强制性提及“不要做选择*”之外,它对我来说还不错。

我的建议:如果它有效并且不会导致性能问题,请留下它并将时间花在其他事情上。如果出现问题,请在将来重新审视。

答案 2 :(得分:1)

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

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进行OR,并假设你还想要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

我怀疑其中任何一个都会比使用distinct / having / group的聚合操作的原件快得多。属性表应该在(property_id,attribute_id)或(attribute_id,property_id)上有多列唯一索引,尽管我假设property_id更具选择性,因此应该是索引中最左边的列。

答案 3 :(得分:0)

使用适当的(和明显的)索引,这在MySQL中非常有效。

选择......

来自产品AS p

INNER JOIN属性a1 ON p.product_id = a1.property_id AND a1.attribute_id = 1
LEFT JOIN属性a2 ON p.product_id = a2.property_id AND a2.attribute_id = 3
LEFT JOIN属性a3 ON p.product_id = a3.property_id AND a3.attribute_id = 7

在哪里(
   情况a1.product_attribute_id为NULL然后0 ELSE 1 END
+ CASE WHEN a1.product_attribute_id为NULL,然后0 ELSE 1 END
)> 0

答案 4 :(得分:0)

如何选择p。*并仅按1列分组?或者这与主键有关吗?

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

相同

WHERE 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