2016-03-07 53 views
1

我有一個產品表和一個更新日誌表。產品表有各種類別(Cat 1,Cat 2,Cat3)和價格水平(Level1,Level2,Level3),我想對這些類別進行計數和分組。所以,我有兩個表中的Mysql內部連接計數

SELECT products.category, 
COUNT(CASE WHEN products.price_level='1' THEN products.category END) as 'Level1', 
COUNT(CASE WHEN products.price_level='2' THEN products.category END) as 'Level2', 
COUNT(CASE WHEN products.price_level='3' THEN products.category END) as 'Level3' 
FROM products 
GROUP BY products.category 
ORDER BY COUNT(products.category) DESC 

結果是:

Category Level1 Level2 Level3 
Cat1  33  14  6 
Cat2  19  29  10 
Cat3  5  17  15 

到目前爲止,good..this工作正常。

現在我想在帶有productId字段的(changelog)中引入另一個表,該字段鏈接到products.id字段。它也有一個字段「狀態」,值爲Active,Inactive)。所以我想狀態字段添加到表中顯示有效的產品,如:

Category Level1 Level2 Level3 Active 
Cat1  33  14  6 
Cat2  19  29  10 
Cat3  5  17  15 

所以我這樣做不工作:

SELECT products.category, 
COUNT(CASE WHEN products.price_level='1' THEN products.category END) as 'Level1', 
COUNT(CASE WHEN products.price_level='2' THEN products.category END) as 'Level2', 
COUNT(CASE WHEN products.price_level='3' THEN products.category END) as 'Level3', 
COUNT(CASE WHEN changelog.status='Active' THEN changelog.status END) as 'Active' 

FROM products 

LEFT JOIN changelog on products.id=changelog.productId 

GROUP BY products.category 
ORDER BY COUNT(products.category) DESC 

計數就會失控,因爲它看來,類別計數可能會累積到更改日誌表中的每個條目。這個查詢有什麼問題?

+0

產品涉及許多更新日誌,反之亦然,所以表之間的cartesean人爲地增加了計數。您需要獲取加入之前生成的計數。 – xQbert

回答

0

您必須在包含多於一個關係的連接之前實現計數。

SELECT P.category, P.level1, p.level2, p.level3, 
COUNT(CASE WHEN changelog.status='Active' THEN changelog.status END) as 'Active' 
FROM (SELECT category, ID 
     COUNT(CASE WHEN price_level='1' THEN category END) as 'Level1', 
     COUNT(CASE WHEN price_level='2' THEN category END) as 'Level2', 
     COUNT(CASE WHEN price_level='3' THEN category END) as 'Level3' 
     FROM products 
     GROUP BY category, ID) P 
LEFT JOIN changelog 
    on p.id=changelog.productId 
ORDER BY COUNT(p.category) DESC 
1

您可以使用相關子查詢此:

SELECT t.category, 
     COUNT(CASE WHEN t.price_level='1' THEN t.category END) as 'Level1', 
     COUNT(CASE WHEN t.price_level='2' THEN t.category END) as 'Level2', 
     COUNT(CASE WHEN t.price_level='3' THEN t.category END) as 'Level3', 
     (SELECT COUNT(CASE 
         WHEN c.status='Active' THEN c.status 
        END) 
     FROM changelog AS c 
     INNER JOIN products AS p ON p.id=c.productId 
     WHERE p.category = t.category) AS 'Active' 
FROM products AS t  
GROUP BY t.category 
ORDER BY COUNT(t.category) DESC 

的子查詢返回的'Active'記載,關聯到當前的產品類別的數量。

+0

該查詢掛起,將mysqld推到100%的CPU。也許因爲changelog表中有500k條記錄? – lilbiscuit

+0

@lilbiscuit您的表格是否已正確編制索引? –

+0

表正確索引?可能不會! – lilbiscuit

0

因爲表更新日誌可以有多個記錄每個產品,它會乘以你已有的計數。解決這個

的方法之一,是通過在一個子查詢,然後您可以加入到查詢的其餘部分從更新日誌表計數活動記錄:

SELECT p.category, 
      SUM(p.price_level='1') as 'Level1', 
      SUM(p.price_level='2') as 'Level2', 
      SUM(p.price_level='3') as 'Level3', 
      COALESCE(c.cnt, 0)  as 'Active' 
FROM  products AS p 
LEFT JOIN (
      SELECT productId, 
        COUNT(*) as cnt 
      FROM  changelog 
      WHERE status = 'Active' 
      GROUP BY productId 
     ) AS c 
     ON c.productId = p.id 
GROUP BY p.category 
ORDER BY COUNT(p.id) DESC 

我也做其他兩個更改:

  • SUM(......)代替COUNT(CASE WHEN...END):它利用一個布爾表達式的計算結果爲0或1的事實;我認爲更清楚,也更短;
  • ORDER BY COUNT(id)而不是ORDER BY COUNT(category):在您分組的字段上應用聚合很奇怪。雖然在MySql中有效,但在標準SQL中它將不被允許。這也是沒有必要的;我發現計算id事件更具可讀性,即使它具有相同的結果。
  • 我沒有使用CASE WHEN子句來過濾活動更新日誌記錄,因爲通過WHERE子句過濾這些記錄的效率更高。