【问题标题】:Mysql inner join with counts in both tablesMysql 内连接与两个表中的计数
【发布时间】:2016-03-07 20:43:02
【问题描述】:

我有一个产品表和一个变更日志表。产品表有各种类别(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

到目前为止,一切都很好..这很好。

现在我想在(更改日志)中引入另一个表,该表具有链接到 products.id 字段的 productId 字段。它还有一个字段“状态”,其值为 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

计数变得混乱,因为看起来更改日志表中每个条目的类别计数可能正在累积。这个查询有什么问题?

【问题讨论】:

  • 产品与许多变更日志相关,反之亦然,因此表之间的笛卡尔人为地增加了计数。您需要获取加入前生成的计数。

标签: mysql


【解决方案1】:

您必须在包含多于 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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 此查询挂起,将 mysqld 推送到 100% CPU。也许是因为 changelog 表中有 500k 条记录?
    • @lilbiscuit 您的表格是否正确编入索引?
    • 表的索引是否正确?可能不会!
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2015-11-25
      • 1970-01-01
      • 1970-01-01
      • 2020-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多