【问题标题】:t-sql group by category and get top n valuest-sql 按类别分组并获取前 n 个值
【发布时间】:2017-01-05 17:08:25
【问题描述】:

想象一下我有这张桌子:

Month | Person | Value
----------------------
Jan   | P1     | 1
Jan   | P2     | 2
Jan   | P3     | 3
Feb   | P1     | 5
Feb   | P2     | 4
Feb   | P3     | 3
Feb   | P4     | 2
...

如何构建一个 t-sql 查询来获取前 2 个值行和第三个值行与其他行的总和?

类似这样的:

RESULT: 
Month | Person | Value
----------------------
Jan   | P3     | 3
Jan   | P2     | 2
Jan   | Others | 1 -(sum of the bottom value - in this case (Jan, P1, 1))
Feb   | P1     | 5
Feb   | P2     | 4
Feb   | Others | 5 -(sum of the bottom values - in this case (Feb, P3, 3) and (Feb, P4, 2))

谢谢

【问题讨论】:

    标签: tsql


    【解决方案1】:

    假设您使用的是 SQL Server 2005 或更高版本,使用 CTE 就可以解决问题。

    • ROW_NUMBER 附加到每一行,从最大值开始,每月重置。
    • SELECT 此查询中每个月的前 2 行(行号
    • UNION 与剩余的行(行号 > 2)

    SQL 语句

    ;WITH Months (Month, Person, Value) AS (
      SELECT 'Jan', 'P1', 1 UNION ALL
      SELECT 'Jan', 'P2', 2 UNION ALL
      SELECT 'Jan', 'P3', 3 UNION ALL
      SELECT 'Feb', 'P1', 5 UNION ALL
      SELECT 'Feb', 'P2', 4 UNION ALL
      SELECT 'Feb', 'P3', 3 UNION ALL
      SELECT 'Feb', 'P4', 2
    ),
    q AS (
      SELECT  Month
              , Person
              , Value
              , RowNumber = ROW_NUMBER() OVER (PARTITION BY Month ORDER BY Value DESC)
      FROM    Months
    )
    SELECT  Month
            , Person
            , Value
    FROM    (        
              SELECT  Month
                      , Person
                      , Value
                      , RowNumber
              FROM    q
              WHERE   RowNumber <= 2 
              UNION ALL
              SELECT  Month
                      , Person = 'Others'
                      , SUM(Value)
                      , MAX(RowNumber)
              FROM    q
              WHERE   RowNumber > 2        
              GROUP BY
                      Month 
            ) q                          
    ORDER BY
            Month DESC
            , RowNumber
    

    Kudo 去找 Andriy 教我一些新技巧。

    【讨论】:

    • @Andriy: 你不可能拥有所有这些......
    【解决方案2】:
    ;WITH atable (Month, Person, Value) AS (
      SELECT 'Jan', 'P1', 1 UNION ALL
      SELECT 'Jan', 'P2', 2 UNION ALL
      SELECT 'Jan', 'P3', 3 UNION ALL
      SELECT 'Feb', 'P1', 5 UNION ALL
      SELECT 'Feb', 'P2', 4 UNION ALL
      SELECT 'Feb', 'P3', 3 UNION ALL
      SELECT 'Feb', 'P4', 2
    ),
    numbered AS (
      SELECT
        Month, Person, Value,
        rownum = ROW_NUMBER() OVER (PARTITION BY Month ORDER BY Value DESC)
      FROM atable
    ),
    grouped AS (
      SELECT
        Month, Person, Value,
        Grp = CASE WHEN rownum < 3 THEN rownum ELSE 3 END
      FROM numbered
    )
    SELECT
      Month,
      Person = CASE Grp WHEN 3 THEN 'Others' ELSE MAX(Person) END,
      Value = SUM(Value)
    FROM grouped
    GROUP BY Month, Grp
    ORDER BY Month DESC, Grp
    

    【讨论】:

    • 如果我没记错的话,OP 需要前 2 个 Value 行(我也忽略了它)。 ORDER BY Person 应该变成 ORDER BY Value。
    • @Lieven:似乎我们都忽略了这一点。 专家...
    【解决方案3】:
    WITH NTable AS
    (
        SELECT [Month],
                       Person,
                       Value,
                       ROW_NUMBER() OVER (PARTITION BY [Month] ORDER BY Value DESC) 
                           AS Rownumber 
                FROM MyTable
    )
    SELECT  t.[Month], 
            CASE Rownumber WHEN 1 THEN t.Person WHEN 2 THEN t.Person ELSE 'Others' END As Person, 
            SUM(t.Value) As [Sum] 
    FROM NTable t
    GROUP BY t.[Month], CASE Rownumber WHEN 1 THEN t.Person WHEN 2 THEN t.Person ELSE 'Others' END
    ORDER BY t.[Month]
    

    【讨论】:

    • 我相信你的意思是 ORDER BY Value DESC(在 CTE 中),否则它会起作用,+1。
    • @Andriy M:我为什么要按价值订购? AFAIK 这些值可以是任何东西。最终的 ORDER BY 可能会发生变化,具体取决于 OP 的需求,因此我专注于获取正确的数据行,使用 ORDER BY 月份只是为了提高可读性。
    • @Andriy M:好的,我明白了,OP 想要前 2 个值。我会改变我的帖子。
    • Andriy 指的是 ROW_NUMBER 函数中的 ORDER BY,而不是结果。
    猜你喜欢
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 2019-04-24
    • 1970-01-01
    • 1970-01-01
    • 2021-10-04
    • 2019-11-20
    • 2013-07-13
    相关资源
    最近更新 更多