【问题标题】:Query: count multiple aggregates per item查询:计算每个项目的多个聚合
【发布时间】:2010-09-21 20:45:24
【问题描述】:

您通常需要显示数据库项目的列表以及每个项目的某些汇总数字。例如,当您在 Stack Overflow 上键入标题文本时,会出现“相关问题”列表。该列表显示相关条目的标题以及每个标题的单个汇总响应数量。

我有类似的问题,但需要多个聚合。我想根据用户选项以 3 种格式中的任何一种显示项目列表:

  • 我的项目名称(共 15 个,我拥有 13 个)
  • 我的项目名称(共 15 个)
  • 我的项目名称(我拥有 13 个)

我的数据库是:

  • items:itemId、itemName、ownerId
  • 类别:catId、catName
  • 地图:mapId、itemId、catId

下面的查询得到:类别名称,每个类别的项目 ID 计数

SELECT
  categories.catName,
  COUNT(map.itemId) AS item_count
FROM categories
LEFT JOIN map
  ON categories.catId = map.catId
GROUP BY categories.catName

此获取:类别名称,仅此 owner_id 的每个类别的项目 ID 计数

SELECT categories.catName, 
COUNT(map.itemId) AS owner_item_count
FROM categories
LEFT JOIN map
  ON categories.catId = map.catId
LEFT JOIN items
  ON items.itemId = map.itemId
WHERE owner = @ownerId
GROUP BY categories.catId

但是我如何在单个查询中同时获取它们?即:类别名称、每个类别的项目 ID 计数、仅此 owner_id 的每个类别的项目 ID 计数

奖金。我怎样才能有选择地只检索其中任何一个 catId count != 0 的位置?在尝试“WHERE item_count 0”时,我得到:

MySQL said: Documentation
#1054 - Unknown column 'rid_count' in 'where clause' 

【问题讨论】:

    标签: mysql sql grouping mysql-error-1054


    【解决方案1】:

    这里有个技巧:计算已知为 1 或 0 的值的 SUM() 等效于值为 1 的行的 COUNT()。您知道布尔比较返回 1 或 0 (或 NULL)。

    SELECT c.catname, COUNT(m.catid) AS item_count,
      SUM(i.ownerid = @ownerid) AS owner_item_count
    FROM categories c
      LEFT JOIN map m USING (catid)
      LEFT JOIN items i USING (itemid)
    GROUP BY c.catid;
    

    至于附加问题,您可以简单地进行内连接而不是外连接,这意味着只会返回在map 中至少有一行的类别。

    SELECT c.catname, COUNT(m.catid) AS item_count,
      SUM(i.ownerid = @ownerid) AS owner_item_count
    FROM categories c
      INNER JOIN map m USING (catid)
      INNER JOIN items i USING (itemid)
    GROUP BY c.catid;
    

    这是另一种解决方案,效率不高,但我会展示它以解释您收到错误的原因:

    SELECT c.catname, COUNT(m.catid) AS item_count,
      SUM(i.ownerid = @ownerid) AS owner_item_count
    FROM categories c
      LEFT JOIN map m USING (catid)
      LEFT JOIN items i USING (itemid)
    GROUP BY c.catid
    HAVING item_count > 0;
    

    您不能在WHERE 子句中使用列别名,因为WHERE 子句中的表达式在选择列表中的表达式之前计算。换句话说,与选择列表表达式关联的值尚不可用。

    您可以在GROUP BYHAVINGORDER BY 子句中使用列别名。这些子句在选择列表中的所有表达式都被求值后运行。

    【讨论】:

      【解决方案2】:

      您可以在 SUM() 中偷偷添加 CASE 语句:

      SELECT categories.catName, 
          COUNT(map.itemId) AS item_count,
          SUM(CASE WHEN owner= @ownerid THEN 1 ELSE 0 END) AS owner_item_count
      FROM categories
      LEFT JOIN map  ON categories.catId = map.catId
      LEFT JOIN items  ON items.itemId = map.itemId
      GROUP BY categories.catId
      HAVING COUNT(map.itemId) > 0
      

      【讨论】:

        【解决方案3】:
        SELECT categories.catName, 
          COUNT(map.itemId) AS item_count,
          COUNT(items.itemId) AS owner_item_count
        FROM categories
        INNER JOIN map
          ON categories.catId = map.catId
        LEFT JOIN items
          ON items.itemId = map.itemId
          AND items.owner = @ownerId
        GROUP BY categories.catId
        

        请注意,您可以在 owner_item_count 上使用 HAVING 子句,但内部连接会为您处理 item_count。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-11-21
          • 2018-12-10
          • 2016-11-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-12
          相关资源
          最近更新 更多