两个表中都有计数的Mysql内部联接

两个表中都有计数的Mysql内部联接,mysql,Mysql,我有一个products表和一个changelog表。products表有各种类别(Cat 1、Cat 2、Cat3)和价格级别(Level1、Level2、Level3),我想对它们进行计数、分组和排序。所以我有 SELECT products.category, COUNT(CASE WHEN products.price_level='1' THEN products.category END) as 'Level1', COUNT(CASE WHEN products.price_lev

我有一个products表和一个changelog表。products表有各种类别(Cat 1、Cat 2、Cat3)和价格级别(Level1、Level2、Level3),我想对它们进行计数、分组和排序。所以我有

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
到目前为止,一切都很好。这个很好用

现在,我想引入另一个表(changelog),它有一个productId字段,链接到products.id字段。它还有一个“状态”字段,其值为Active(活动)、Inactive(不活动)。因此,我想将status字段添加到显示有效产品的表中,如下所示:

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

计数失控,因为似乎changelog表中的每个条目的类别计数都在累积。此查询有什么问题?

必须在包含多个1-1关系的联接之前具体化计数

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

您可以为此使用相关子查询:

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'
记录的计数。

因为表changelog可以对每个产品有多条记录,所以它将乘以您已经拥有的计数

解决此问题的一种方法是在子查询中计算changelog表中的活动记录,然后将其加入查询的其余部分:

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(在…结束时的情况)
    :它利用布尔表达式的计算结果为0或1的事实;在我看来,它更清晰,也更短
  • 按计数排序(id)
    而不是
    按计数排序(类别)
    :在分组依据的字段上应用聚合是很奇怪的。虽然在MySql中有效,但在标准SQL中它是不允许的。也没有必要,;我发现计数
    id
    出现次数更具可读性,即使它有相同的结果
  • 我没有使用
    CASE WHEN
    子句来过滤活动的变更日志记录,因为通过
    WHERE
    子句过滤这些记录更有效

产品与许多变更日志相关,反之亦然,因此表之间的cartesean人为地增加了计数。您需要在加入之前获取生成的计数。此查询挂起,将mysqld推到100%CPU。可能是因为changelog表中有500k条记录?@lilbique您的表是否正确索引?这些表是否正确索引?可能不会!