【问题标题】:T-SQL grouped sum, based on help tableT-SQL 按总和分组,基于帮助表
【发布时间】:2017-12-06 19:51:53
【问题描述】:

我有一个表,其中包含一个分组列和一个数字列,我想对其进行汇总。我的桌子是这样的:

  Person   Object    Count  
 -------- --------- ------- 
  John     apples        1  
  John     pears         6  
  John     ferrari      10  
  Mike     porsche       3  
  Mike     ferrari       1  

我想创建一个表,每人返回一行,并返回包含一组对象的几列。在这个例子中,苹果和梨都是水果,而法拉利和保时捷都是汽车。我的结果表如下所示:

  Person   Fruits   Cars  
 -------- -------- ------ 
  John          7     10  
  Mike          0      4  

我有一个帮助表,将每个对象分配给一个组。组的数量是一成不变的,但我可能会在每个组中添加更多对象。这就是我创建这个帮助表的原因,这样当我的数据中有新对象时,我可以简单地添加一行。例如,我以后可能会添加李子(也是一种水果)或丰田(也是一种汽车),但我绝不会添加既不是汽车也不是水果的东西。

  Object    Group   
 --------- -------- 
  apples    fruits  
  pears     fruits  
  ferrari   cars    
  porsche   cars    

我的解决方案是以下查询:

SELECT
  Person,
  SUM(CASE WHEN Object IN (SELECT Object FROM help_table WHERE Group = 'fruits') THEN Count END) AS Fruits,
  SUM(CASE WHEN Object IN (SELECT Object FROM help_table WHERE Group = 'cars') THEN Count END) AS Cars
FROM start_table
GROUP BY Person

它返回以下错误:

Cannot perform an aggregate function on an expression containing an aggregate or a subquery.

我正在寻找其他解决方案来达到预期的结果。在查询中指定哪些对象属于每个组很容易,但我更喜欢将新对象添加到表中,而不必将它们硬编码。

【问题讨论】:

    标签: sql-server tsql sum case


    【解决方案1】:

    这适用于一小部分有限的组

    SELECT
      Person,
      COUNT(hf.Object),
      COUNT(hc.Object)
    FROM 
      start_table s
      LEFT join
      help_table hf ON s.Object = hf.Object AND hf.Group = 'fruits'
      LEFT join
      help_table hc ON s.Object = hc.Object AND hc.Group = 'cars'
    GROUP BY Person
    

    动态地为每个组添加一个新列变得更加棘手,因为 SQL 并不真正支持任意列输出

    【讨论】:

    • 我做了一个小提琴来测试你的解决方案:sqlfiddle.com/#!6/cf0ae/2。它并没有得到想要的结果,因为它不是求和,而是对不同类型的对象进行计数。
    • @A.Stam 很高兴为您提供帮助。将子查询传输到 JOIN 的主要位,以便 ypu 可以聚合它
    【解决方案2】:

    创建两张桌子,水果和汽车:

    SELECT
      s.Person,
      sum(fruits.count) as Fruits,
      sum(cars.count) AS Cars
    FROM start_table s
        left join help_table fruits on s.[Object] = fruits.[Object] and fruits.[Group] = 'fruits'
        left join help_table cars on s.Object = cars.Object and cars.[Group] = 'cars'
    GROUP BY s.Person
    

    【讨论】:

      【解决方案3】:

      您可以使用 sum 和 pivot 如下:

       Select * from (
           Select [Person], [Group], sum([Count]) as sm from start_table s
              left join help_table h
              on s.[Object] = h.[Object]
              group by [Person], [Group]
          ) a
          pivot (sum(sm) for [Group] in ([fruits],[cars])) p
      

      [Group]的动态列表可以如下查询

      Declare @cols1 varchar(max)
      Declare @query nvarchar(max)
      
      Select @cols1 = stuff((select distinct ','+QuoteName([Group]) from help_table  for xml path('')),1,1,'')
      
      Set @query = '   Select * from (
               Select [Person], [Group], [Count] from start_table s
                  left join help_table h
                  on s.[Object] = h.[Object]
              ) a
              pivot (sum([Count]) for [Group] in (' + @cols1 + ')) p '
      
      Exec sp_executesql @query
      

      【讨论】:

      • 您还可以在子查询中删除聚合,因为聚合是在枢轴本身中处理的。在动态查询中更新。
      【解决方案4】:

      您可以使用公用表表达式轻松获取此信息。下面的查询将以所需的格式给出结果。我还在here 创建了一个sqlfiddle。有时 SqlFiddle 无法正确加载,以防在链接上重试。请随意看看。

      SELECT
        Person,
        ISNULL(Cars, 0) Cars,
        ISNULL(Fruits, 0) Fruits
      FROM (SELECT
        Person,
        [Group],
        SUM([Count]) AS TotalCount
      FROM Userdata
      JOIN ItemGroup
        ON ItemGroup.Item = Userdata.Item
      GROUP BY Person,
               [Group]) x
      PIVOT
      (
      SUM(TotalCount)
      FOR [Group] IN ([fruits], [cars])
      ) pvt
      

      【讨论】:

      • 这会为每个人返回多行。您的结果表与预期结果不符。
      • 是的,你是对的。您可以在以下位置找到更新的小提琴:sqlfiddle.com/#!6/dc056/2
      猜你喜欢
      • 1970-01-01
      • 2012-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-16
      • 2010-10-13
      相关资源
      最近更新 更多