2016-07-21 62 views
1

我试图增强在商家网站中找到的SQL错误。在WHERE子句中避免多个SELECT

我有两个表:

------ Table "products" ------ 
| id_product | product_name | 
----------------------------- 
|   1 |  T-shirt | 
|   2 |  Trousers | 
|   3 | Sweat-shirt | 
|   4 |  Socks | 

----------- Table "features" ----------- 
| id_product | feature | feature_value | 
---------------------------------------- 
|   1 | Color |   Red | 
|   1 | Size |    M | 
|   1 | Fabric |  Cotton | 
|   2 | Color |   Blue | 
|   2 | Size |    S | 
|   2 | Fabric |  Polyester | 
|   3 | Color |   Red | 
|   3 | Size |    L | 
|   3 | Fabric |   Wool | 
|   4 | Color |   White | 
|   4 | Size |    L | 
|   4 | Fabric |  Cotton | 

我想,具有以下特点检索产品:

  • 其中颜色是红色或蓝色
  • 其中大小为M
  • 其中织物是棉花

我的查询如下:

SELECT p.id_product 
FROM products p 
WHERE p.id_product IN (SELECT f.id_product FROM features f WHERE f.feature_value IN ("Red", "Blue")) 
    AND p.id_product IN (SELECT f.id_product FROM features f WHERE f.feature_value = "M") 
    AND p.id_product IN (SELECT f.id_product FROM features f WHERE f.feature_value = "Cotton") 
GROUP BY p.id_product 

(当然,在现实中,我的表,我的查询是waaaay比这更复杂,我只是专注于问题的一部分)

的多个SELECT在WHERE子句中,如果选择了8个或更多功能,则会导致整个服务器变慢。 有没有办法避免在WHERE子句中做这么多查询?

编辑:举例来说,这里是真正的查询之一:

SELECT p.id_product id_product 
FROM ps_product p 
INNER JOIN ps_category_product cp ON p.id_product = cp.id_product 
INNER JOIN ps_category c ON (c.id_category = cp.id_category AND c.nleft >= 6 AND c.nright <= 7 AND c.active = 1) 
LEFT JOIN ps_stock_available sa ON (sa.id_product = p.id_product AND sa.id_shop = 1) 
INNER JOIN ps_product_shop product_shop ON (product_shop.id_product = p.id_product AND product_shop.id_shop = 1) 
WHERE 1 
AND product_shop.active = 1 
AND product_shop.visibility IN ("both", "catalog") 
AND p.id_manufacturer IN (5,4) 
AND sa.quantity > 0 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 82) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 37248) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 181) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 37821) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 33907) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 33902) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 70) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 76) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 291) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 75) 
AND p.id_product IN (SELECT id_product FROM ps_feature_product fp WHERE fp.id_feature_value = 44459) 
GROUP BY id_product 
+0

加入和过滤在哪里呢? – Pred

+0

实际上,在查询中已经有很多JOIN,我真的希望找到一个解决方案,让我只编辑WHERE子句。 – roberto06

+0

所以,很多加入意味着,你不会加入最后一张桌子,因为......? – Pred

回答

3

你可以使用JOINHAVING条款:

SELECT p.id_product 
FROM products p 
JOIN features f 
    ON p.id_product = f.id_product 
GROUP BY p.id_product 
HAVING COUNT(CASE WHEN f.feature_value IN ('Red', 'Blue') THEN 1 END) > 0 
    AND COUNT(CASE WHEN f.feature_value = 'M' THEN 1 END) > 0 
    AND COUNT(CASE WHEN f.feature_value = 'Cotton' THEN 1 END) > 0; 

LiveDemo


甚至更​​短(MySQL的):

HAVING SUM(f.feature_value IN ('Red', 'Blue')) > 0 
    AND SUM(f.feature_value = 'M') > 0 
    AND SUM(f.feature_value = 'Cotton') > 0; 
+1

没有想到'HAVING',它似乎工作,谢谢! – roberto06

+0

我很喜欢Pred的解决方案,特别是如果你有很多特性和值要比较的话,就像OP说的那样。此外,我做了一些测试,随机创建了5000个产品,200个不同的功能,然后将这些功能合并到功能表中的大约1毫米记录中,并随机使用了feature_values。 Preds解决方案在大约20ms内运行,这个需要大约280ms,并且占用更多的CPU,因为排序和分组是在过滤之前完成的,而不是其他方式。 (这是在添加PK和FK和MSSQL2014后,但我认为MySQL会是相似的。) – deroby

+1

@deroby感谢您的测试和此评论。为了记录,我的解决方案比这个有更多的局限性。在条件更复杂的情况下工作可能很难甚至不可能。 (复杂性=条件数量)。 – Pred

0

试试这个,希望这将有助于。

SELECT p.id_product,count(f.feature_value) 
FROM products p, features f 
where f.id_product=p.id_product 
and f.feature_value in ("Red","Blue") 
or f.feature_value = "M" 
or f.feature_value = "Cotton" 
group by p.id_product 
1
SELECT 
    P.id_product 
FROM 
    products P 
    INNER JOIN features F 
    ON P.id_product = F.id_product 
WHERE 
    (F.featurure = 'Color' AND F.feature_value IN ('red', 'blue')) 
    OR (F.featurure = 'Size' AND F.feature_value IN ('M')) 
    OR (F.featurure = 'Fabric' AND F.feature_value IN ('Cotton')) 
GROUP BY 
    P.id_product 
HAVING 
    COUNT(DISTINCT F.feature) = 3 

HAVING条件告诉,应至少有3个不同的匹配特征(这是您的搜索字段的计数)。