简体   繁体   English

在WHERE子句中避免多个SELECT

[英]Avoid multiple SELECT in WHERE clause

I'm trying to enhance a SQL bug I found in a merchant website. 我正在尝试增强在商家网站中发现的SQL错误。

I have two tables : 我有两个表:

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

I'm trying to retrieve the products with the following features : 我正在尝试检索具有以下功能的产品:

  • where color is either red or blue 颜色是红色或蓝色
  • where size is M 尺寸是M
  • where fabric is cotton 面料是棉的

My query is as follows : 我的查询如下:

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

(Of course, in reality, my tables and my query are waaaay more complex than that, I'm just focusing on the problematic part) (当然,实际上,我的表和查询要比这复杂得多,我只关注有问题的部分)

The multiple SELECT in the WHERE clause causes my entire server to slow down if 8 or more features are selected. 如果选择8个或更多功能,则WHERE子句中的多个SELECT会导致我的整个服务器速度变慢。 Is there a way to avoid making this many queries in the WHERE clause ? 有没有办法避免在WHERE子句中进行这么多查询?

EDIT : For instance, here's one of the real queries : 编辑:例如,这是真正的查询之一:

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

You could use JOIN and HAVING clause: 您可以使用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


Or even shorter (MySQL): 甚至更短(MySQL):

HAVING SUM(f.feature_value IN ('Red', 'Blue')) > 0
   AND SUM(f.feature_value = 'M') > 0
   AND SUM(f.feature_value = 'Cotton') > 0;
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

The HAVING condition tells, that there should be at least 3 different matching features (which is the count of your search fields). HAVING条件表明,至少应有3个不同的匹配功能(这是您的搜索字段的数量)。

Try this, Hope it will help. 试试这个,希望对您有所帮助。

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

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

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