【问题标题】:How to combine GROUP BY and ROW_NUMBER?如何组合 GROUP BY 和 ROW_NUMBER?
【发布时间】:2012-03-25 22:24:09
【问题描述】:

我希望下面的示例代码是不言自明的:

declare @t1 table (ID int,Price money, Name varchar(10))
declare @t2 table (ID int,Orders int,  Name varchar(10))
declare @relation  table (t1ID int,t2ID int)
insert into @t1 values(1, 200, 'AAA');
insert into @t1 values(2, 150, 'BBB');
insert into @t1 values(3, 100, 'CCC');
insert into @t2 values(1,25,'aaa');
insert into @t2 values(2,35,'bbb');
insert into @relation values(1,1);
insert into @relation values(2,1);
insert into @relation values(3,2);

select T2.ID AS T2ID
,T2.Name as T2Name
,T2.Orders
,T1.ID AS T1ID
,T1.Name As T1Name
,T1Sum.Price
FROM @t2 T2
INNER JOIN (
    SELECT Rel.t2ID
        ,MAX(Rel.t1ID)AS t1ID 
-- the MAX returns an arbitrary ID, what i need is: 
--      ,ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList
        ,SUM(Price)AS Price
        FROM @t1 T1 
        INNER JOIN @relation Rel ON Rel.t1ID=T1.ID
        GROUP BY Rel.t2ID
)AS T1Sum ON  T1Sum.t2ID = T2.ID
INNER JOIN @t1 T1 ON T1Sum.t1ID=T1.ID

结果:

T2ID   T2Name   Orders  T1ID    T1Name  Price     
 1      aaa       25     2       BBB    350,00     
 2      bbb       35     3       CCC    100,00

上面评论了我需要的东西,这是一种获取ROW_NUMBER 的方法,但也是首先获取Group By 的方法。所以我需要在关系表中按T2.ID 分组的所有T1 价格中的sum,在外部查询中需要最高价格的t1ID

换句话说:如何将MAX(Rel.t1ID)AS t1ID 更改为返回价格最高的ID?

所以想要的结果是(注意第一个 T1ID 从 2 变为 1,因为它的价格更高):

T2ID   T2Name   Orders  T1ID    T1Name  Price     
 1      aaa       25     1       AAA    350,00     
 2      bbb       35     3       CCC    100,00

注意:如果您想知道为什么我不将 Orders 与价格相乘:它们不是真实的(所以我应该离开这个专栏,因为它有点模棱两可,请忽略它,我刚刚添加它以减少抽象)。实际上Orders 必须保持不变,这就是子查询方法加入两者的原因,也是我首先需要分组的原因。

结论:显然我的问题的核心可以通过OVER clause 来回答,该OVER clause 可以应用于任何 聚合函数,如SUM(见Damien's answer ) 什么对我来说是新的。谢谢大家的工作方法。

【问题讨论】:

  • 最终结果中不应该是AAA而不是BBB吗?

标签: sql sql-server-2005 tsql group-by


【解决方案1】:

哇,其他答案看起来很复杂 - 所以我希望我没有遗漏一些明显的东西。

您可以对聚合使用OVER/PARTITION BY,然后它们将在没有GROUP BY 子句的情况下进行分组/聚合。所以我只是将您的查询修改为:

select T2.ID AS T2ID
    ,T2.Name as T2Name
    ,T2.Orders
    ,T1.ID AS T1ID
    ,T1.Name As T1Name
    ,T1Sum.Price
FROM @t2 T2
INNER JOIN (
    SELECT Rel.t2ID
        ,Rel.t1ID
 --       ,MAX(Rel.t1ID)AS t1ID 
-- the MAX returns an arbitrary ID, what i need is: 
      ,ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList
        ,SUM(Price)OVER(PARTITION BY Rel.t2ID) AS Price
        FROM @t1 T1 
        INNER JOIN @relation Rel ON Rel.t1ID=T1.ID
--        GROUP BY Rel.t2ID
)AS T1Sum ON  T1Sum.t2ID = T2.ID
INNER JOIN @t1 T1 ON T1Sum.t1ID=T1.ID
where t1Sum.PriceList = 1

它给出了请求的结果集。

【讨论】:

  • 哇,这就是我所希望的,而其他答案正是我所担心的。谢谢(全部),我必须仔细看看如何将它安装到我的实际查询中(我的样本非常减少了)。
  • 谢谢!我一直在试图弄清楚如何使它适用于几个不同的窗口函数。我一直把总和放在 partition by 子句中,而不是放在开头!
【解决方案2】:

毫无疑问,这可以简化,但结果符合您的期望。

主要是为了

  • 在单独的CTE 中计算每个t2ID 的最高价格
  • 在单独的CTE 中计算每个t2ID 的总价
  • 结合CTE的两个结果

SQL 语句

;WITH MaxPrice AS ( 
    SELECT  t2ID
            , t1ID
    FROM    (       
                SELECT  t2.ID AS t2ID
                        , t1.ID AS t1ID
                        , rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t1.Price DESC)
                FROM    @t1 t1
                        INNER JOIN @relation r ON r.t1ID = t1.ID        
                        INNER JOIN @t2 t2 ON t2.ID = r.t2ID
            ) maxt1
    WHERE   maxt1.rn = 1                            
)
, SumPrice AS (
    SELECT  t2ID = t2.ID
            , Price = SUM(Price)
    FROM    @t1 t1
            INNER JOIN @relation r ON r.t1ID = t1.ID
            INNER JOIN @t2 t2 ON t2.ID = r.t2ID
    GROUP BY
            t2.ID           
)           
SELECT  t2.ID
        , t2.Name
        , t2.Orders
        , mp.t1ID
        , t1.ID
        , t1.Name
        , sp.Price
FROM    @t2 t2
        INNER JOIN MaxPrice mp ON mp.t2ID = t2.ID
        INNER JOIN SumPrice sp ON sp.t2ID = t2.ID
        INNER JOIN @t1 t1 ON t1.ID = mp.t1ID

【讨论】:

    【解决方案3】:
    ;with C as
    (
      select Rel.t2ID,
             Rel.t1ID,
             t1.Price,
             row_number() over(partition by Rel.t2ID order by t1.Price desc) as rn
      from @t1 as T1
        inner join @relation as Rel
          on T1.ID = Rel.t1ID
    )
    select T2.ID as T2ID,
           T2.Name as T2Name,
           T2.Orders,
           T1.ID as T1ID,
           T1.Name as T1Name,
           T1Sum.Price
    from @t2 as T2
      inner join (
                  select C1.t2ID,
                         sum(C1.Price) as Price,
                         C2.t1ID
                  from C as C1
                    inner join C as C2 
                      on C1.t2ID = C2.t2ID and
                         C2.rn = 1
                  group by C1.t2ID, C2.t1ID
                 ) as T1Sum
        on T2.ID = T1Sum.t2ID
      inner join @t1 as T1
        on T1.ID = T1Sum.t1ID
    

    【讨论】:

      【解决方案4】:

      重复数据删除(选择最大 T1)和聚合需要作为不同的步骤进行。我使用了 CTE,因为我认为这更清楚:

      ;WITH sumCTE
      AS
      (
          SELECT  Rel.t2ID, SUM(Price) price
          FROM    @t1         AS T1
          JOIN    @relation   AS Rel 
          ON      Rel.t1ID=T1.ID
          GROUP 
          BY      Rel.t2ID
      )
      ,maxCTE
      AS
      (
          SELECT  Rel.t2ID, Rel.t1ID, 
                  ROW_NUMBER()OVER(Partition By Rel.t2ID Order By Price DESC)As PriceList
          FROM    @t1         AS T1
          JOIN    @relation   AS Rel 
          ON      Rel.t1ID=T1.ID
      )
      SELECT T2.ID AS T2ID
      ,T2.Name as T2Name
      ,T2.Orders
      ,T1.ID AS T1ID
      ,T1.Name As T1Name
      ,sumT1.Price
      FROM    @t2 AS T2
      JOIN    sumCTE AS sumT1
      ON      sumT1.t2ID = t2.ID
      JOIN    maxCTE AS maxT1
      ON      maxT1.t2ID = t2.ID
      JOIN    @t1 AS T1
      ON      T1.ID = maxT1.t1ID
      WHERE   maxT1.PriceList = 1
      

      【讨论】:

        猜你喜欢
        • 2015-04-06
        • 2011-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多