SQL Server分组行

时间:2017-06-12 20:42:37

标签: sql tsql sql-server-2014

我有这些数据:

{{1}}

我想要一个脚本,在条件上生成三个或四个组:每个组的总和(计数)< 50

我想要这个输出:

{{1}}

2 个答案:

答案 0 :(得分:3)

假设必须为每个name执行此操作,您可以使用递归cte。

with rownums as (select t.*,row_number() over(partition by name order by id) as rnum from t)
,cte(rnum,id,name,cnt,runningsum,grp) as  
(select rnum,id,name,cnt,cnt,1  from rownums where rnum=1
 union all
 select t.rnum,t.id,t.name,t.cnt
 ,case when c.runningsum+t.cnt > 50 then t.cnt else c.runningsum+t.cnt end
 ,case when c.runningsum+t.cnt > 50 then t.id else c.grp end
 from cte c
 join rownums t on t.rnum=c.rnum+1 and t.name=c.name
) 
select id,cnt,name,dense_rank() over(partition by name order by grp) as grp
from cte

Sample Demo

跟踪运行总和并在超过50时重置它。当总和超过50时,请记住id。这可用于分配组号。

答案 1 :(得分:1)

对于计数小于50的记录,我们可以通过计算计数的运行总数来简单地生成分组ID,然后将此运行总计除以50.但是,由于某些记录可能已经计数大于或等于50可能会生成错误的ID。为了解决这个问题,我们需要以某种方式强制在下一条记录上生成新的分组ID。如果当前记录计数为50或更大,则可以通过简单地将下一记录的计数调整50来完成。 以下示例演示了如何完成此操作:

CREATE TABLE #Items
(
     [Id]       INT NOT NULL PRIMARY KEY
    ,[Name]     VARCHAR(50) NOT NULL
    ,[Count]    INT NOT NULL
)

INSERT INTO #Items
VALUES 
(1, 'cdd', 50 ),         
(2, 'cdd', 15 ),       
(3, 'cdd', 0  ),        
(4, 'cdd', 25 ),         
(5, 'cdd', 11 );       

;WITH CTE_ItemCountsAdjusted
AS
(
    SELECT   [Id]       
            ,[Name] 
            ,[Count]
            ,LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) AS PrevCount
            ,(
                CASE 
                    WHEN LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) >= 50 THEN [Count] + 50
                    ELSE [Count]
                END
            ) AdjustedCount
    FROM    #Items
)
SELECT   [Id]       
        ,[Name] 
        ,[Count]
        ,SUM([AdjustedCount]) OVER (PARTITION BY [Name] ORDER BY [Id] ROWS UNBOUNDED PRECEDING) / 50 AS [Group_number]
FROM    CTE_ItemCountsAdjusted
ORDER BY    [Id]   

此方法消除了递归调用的需要。请注意,如果您需要组ID严格按顺序排列(组号之间没有间隙),那么您可以使用DENSE_RANK()窗口函数来实现此功能,如下例所示:

INSERT INTO #Items
VALUES 
(1, 'cdd', 50 ),         
(2, 'cdd', 15 ),       
(3, 'cdd', 0  ),        
(4, 'cdd', 25 ),         
(5, 'cdd', 11 ),      
(6, 'cdd', 200 ),
(7, 'cdd', 10 );  

;WITH CTE_ItemCountsAdjusted
AS
(
    SELECT   [Id]       
            ,[Name] 
            ,[Count]
            ,LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) AS PrevCount
            ,(
                CASE 
                    WHEN LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) >= 50 THEN [Count] + 50
                    ELSE [Count]
                END
            ) AdjustedCount
    FROM    #Items

),CTE_ItemCountsWithGroupID
AS
(
    SELECT   [Id]       
            ,[Name] 
            ,[Count]
            ,SUM([AdjustedCount]) OVER (PARTITION BY [Name] ORDER BY [Id] ROWS UNBOUNDED PRECEDING) / 50 AS [Group_number]
    FROM    CTE_ItemCountsAdjusted  
)
SELECT   [Id]       
        ,[Name] 
        ,[Count]
        ,[Group_number]