[英]Optimize MySQL COUNT / group by query: show only categories that have products associated
select COUNT(p.id) AS `num`, cat.id, cat.name, cat.parent_id AS `parent_id`
from products p
INNER JOIN `products_categories` AS `pc` ON p.id=pc.products_id
INNER JOIN `categories` AS `cat` ON pc.categories_id=cat.id
WHERE p.status = 1 AND p.gender IN ('female','neutral')
group by cat.id
說明查詢:
1 SIMPLE p ref PRIMARY,gender,status status 1 const 139107 Using where; Using temporary; Using filesort
1 SIMPLE pc ref products_id,categories products_id 4 mydb.p.id 1 Using index
1 SIMPLE cat eq_ref PRIMARY,categoryname PRIMARY 4 mydb.pc.categories_id 1 Using where
相關指標:
products 0 PRIMARY 1 id A 299339 BTREE
products 1 title 1 title A 299339 BTREE
products 1 sku 1 sku A 299339 BTREE
products 1 body 1 body A 299339 200 BTREE
products 1 short_description 1 short_description A 299339 200 YES BTREE
products 1 keywords 1 keywords A 2 200 BTREE
products 1 gender 1 gender A 10 BTREE
products 1 status 1 status A 2 BTREE
products 1 brand_id 1 brand_id A 3741 YES BTREE
products 1 merchant 1 merchant_id A 52 BTREE
products 1 title_2 1 title,body,keywords 299339 FULLTEXT
products 1 title_3 1 title 299339 FULLTEXT
products 1 body_2 1 body 299339 FULLTEXT
products_categories 0 PRIMARY 1 id A 514054 BTREE
products_categories 1 products_id 1 products_id, categories_id A 514054 BTREE
products_categories 1 categories 1 categories_id A 266 BTREE
categories 0 PRIMARY 1 id A 154 BTREE
categories 1 categoryname 1 name A 154 BTREE
這是一個包含產品,類別和它們之間的N:N關系的數據庫。 產品可以是1個或多個類別。
我基本上需要一個查詢來告訴我,對於該類別當前是否具有任何產品(對於我來說,我可以隱藏沒有產品的類別),對於當前的產品過濾器(在本例中為狀態和性別)。 目前,我對每個類別中的產品進行計數以了解這一點。
查詢WHERE參數將根據用戶選擇的過濾器而變化,因此該部分在此優化中不是很重要。
我不需要類別中產品的確切數量,即使它們是否具有產品也是如此。 Products表具有很多索引,具有products_categories和category表。 產品表包含約40萬個產品,150個類別和50萬個product_categories。
MySQL 5.6.22托管在AWS RDS上,InnoDB中的所有表。
我了解我的解釋查詢顯示了為什么它運行緩慢(瀏覽了很多產品),但是我不知道如何優化它……也許是考慮問題的另一種方式?
對於此查詢:
select COUNT(p.id) AS `num`, cat.id, cat.name, cat.parent_id AS `parent_id`
from products p INNER JOIN
products_categories `pc`
ON p.id = pc.products_id INNER JOIN
categories cat
ON pc.categories_id = cat.id
WHERE p.status = 1 AND p.gender IN ('female', 'neutral')
group by cat.id;
您需要所有join
鍵上的索引。 我會推薦products(status, gender, id)
, products_categories(products_id, categories_id)
和categories(id)
。
有時,在MySQL中,使用相關子查詢比使用group by
更快,方法group by
:
select c.*,
(select count(*)
from products_categories `pc` INNER JOIN
products p
ON p.id = pc.products_id
where pc.categories_id = cat.id AND
p.status = 1 AND p.gender IN ('female', 'neutral')
) as cnt
from categories c;
這個版本需要在products_categories(categories_id, products_id)
和products(id, status, gender)
上的索引。
您的查詢返回139107條匹配記錄,因為您使用的過濾條件不是很嚴格(狀態= 1,性別=女性或中性)。
SELECT cat.id, cat.name, cat.parent_id AS `parent_id`,
COUNT(p.id) AS `num`
FROM `categories` AS `cat`
INNER JOIN `products_categories` AS `pc` ON pc.categories_id=cat.id
INNER JOIN products AS p ON p.id=pc.products_id
WHERE p.status = 1 AND p.gender IN ('female','neutral')
GROUP BY cat.id
HAVING COUNT(p.id)>0
添加HAVING
不會自動改善查詢。 問題是您的過濾條件會返回許多匹配的產品。 按性別或布爾狀態(真/假)過濾記錄可能會導致表掃描,因為值重復很多,即使狀態和性別是索引,MySQL仍可能認為運行表掃描比使用索引便宜。
HAVING
用於過濾沒有產品的任何類別。 嘗試這個
SELECT cat.id, cat.name, cat.parent_id AS `parent_id`,
COUNT(pc._products_id) AS `num`
FROM `categories` AS `cat`
INNER JOIN `products_categories` AS `pc` ON pc.categories_id=cat.id
GROUP BY cat.id
HAVING COUNT(pc.products_id)>0
上面的查詢將不與產品表聯接。 如果它們具有產品關聯,則僅查看product_categories。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.