2016-07-21 3 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 

다중 SELECT에게 (물론, 현실에서, 내 테이블과 내 쿼리 난 그냥 문제가있는 부분에 초점을 맞추고있어, waaaay를보다 더 복잡) : 다음과 같이내 쿼리입니다 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

JOIN은 무엇이고 필터링은 어디에서 할 수 있습니까? – 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 개의 고유 한 기능을 무작위로 생성 한 다음 임의의 feature_values가있는 features 테이블의 약 1 밀론 레코드로 조합했습니다. Preds 솔루션은 약 20ms 만에 실행되며 약 280ms가 소요되며 정렬 및 그룹화가 다른 방법 대신 필터링 전에 수행되기 때문에 훨씬 많은 CPU가 필요합니다. (이것은 PKs와 FK를 추가 한 후 MSSQL2014에 있지만, mysql이 비슷하다고 생각합니다.) – deroby

+1

@deroby 테스트와이 주석에 감사드립니다. 기록을 위해, 나의 해결책은 이것보다 더 많은 한계를 가지고있다. 조건이 더 복잡 할 때 작동하게하는 것은 어렵거나 불가능할 수 있습니다. (복잡성! = 조건의 양 tho). – 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 개의 서로 다른 일치하는 기능 (검색 필드의 수)이 있어야한다고 알려줍니다.

관련 문제