SQL Group按特定金额

时间:2015-03-25 20:01:46

标签: sql algorithm tsql sql-server-2014

我有一张订单表,其中包含客户ID和订单金额。我想加入这些订单但加入订单不能超过一定数量。以下示例:

假设最大金额为33个托盘,我有一个这样的表:

Order ID    Client ID   Amount  
1           100001        10    
2           100001        22    
3           100001        13    
4           100001        33    
5           100001        1    
6           100001        5     
7           100001        6    

结果应为:

Order ID    Client ID   Amount  Joined ID   Joined Amount
1            100001       10    100001A         32
2            100001       22    100001A         32
3            100001       13    100001B         13
4            100001       33    100001C         33
5            100001       1     100001D         12
6            100001       5     100001D         12
7            100001       6     100001D         12

在这里,如果我们也可以想出一种方法,将订单号为5,6,7的广告订单加入订单10001B,那就太棒了。但即使这个解决方案也足够了。

我对如何解决这个问题有一些想法,但我无法提出一个有效的解决方案。我会像这样处理大约2000个Order Ids,所以我也不希望这是一个缓慢的操作。我正在使用SQL Server 2014

3 个答案:

答案 0 :(得分:0)

不确定我是否正确理解了这个问题,但您可以尝试

select [Client ID], [Joined ID], sum([Amount]) as Total_Amount
from [table_name]
group by [Client ID], [Joined ID]
having sum([Amount]) <= 33

它不包含订单ID,但由于它看起来是唯一的,因此您无法在组中使用它。

编辑是在查询中添加having子句,说我们不能添加超过33的内容。

答案 1 :(得分:0)

我尝试用简单的选择解决而不使用显式游标,但这样做有点困难。 我已经解决了它,并得到了你想要的东西: TempTablecursorcounter,用于检查后续金额的总和,CHAR()生成字母的函数;我计算了这些值,并插入到临时表中,最后更新了临时表,以下是我尝试过的DEMO IS HERE

create table #tbl_name
       (OrderID int, 
        ClientID int,
        Amount int, 
        joinedId varchar(15) ,
        joinedAmount int)

insert #tbl_name(OrderID,ClientID,Amount) 
select OrderID,ClientID,Amount from tbl_name

declare cr cursor for  
           select orderId, 
             clientId, 
             amount  
           from tbl_name 
           order by OrderId

declare @summedAmount int, 
        @orderId int,
        @clientId int,
        @amount int,
        @counter int

set @summedAmount=0
set @counter=65


open cr
fetch from cr into @orderId,@clientId,@amount
while (@@fetch_status=0)
begin
        if (@amount + @summedAmount < 33)
        begin
            set @summedAmount=@summedAmount+@amount
            update #tbl_name 
            set    joinedId=cast(@ClientId as varchar(10))+char(@counter),
                   joinedAmount=@summedAmount
            where  orderId=@orderId
        end
        else if (@amount + @summedAmount >33)
        begin
            set @counter=@counter+1
            set @summedAmount=@amount
            update #tbl_name 
            set    joinedId=cast(@ClientId as varchar(10))+char(@counter),
                   joinedAmount=@Amount
            where  orderId=@orderId
        end
        fetch from cr into @orderId,@clientId,@amount
end
close cr
deallocate cr
go

with CTE as
(
    select JoinedId, max(joinedAmount) mx
    from #tbl_name
    group by JoinedId
)
update #tbl_name
set joinedAmount = CTE.mx
from #tbl_name
join CTE on #tbl_name.JoinedId=CTE.JoinedId

select * from #tbl_name 

drop table #tbl_name

答案 2 :(得分:0)

你可以在这里借助递归CTE找到建议的解决方案(sql定义):http://sqlfiddle.com/#!6/285c16/45

basicaly CTE迭代有序列表(通过clientID,orderID)并评估总和金额是否超过33。

我已将下一个clientID添加到模拟数据,以测试正确的子级别标准评估。

这里是查询以获得结果:

-- prepare numbering for iteration
with orders_nr
as 
(
  select row_number() over(order by clientID, id) as [nr],
     o.*
  from orders o

)

,
-- prepare sum totals
re
as
(
  select  id, amount, amount as amount_total ,o.[nr] as nr,
    clientID
  from orders_nr o
  where o.[nr]=1
  UNION ALL
  select o.id, o.amount, 
    CASE WHEN o.clientID <> r.clientID then o.amount
    ELSE o.amount+ r.amount_total END,
    o.[nr] as nr, o.clientID
  from orders_nr o  join re r
    on (o.[nr]=r.[nr]+1)

) 
,

 -- iterate total - evaluate current criteria (<=33)
 re2 as
 (
   select re.id, re.amount, re.amount_total, 
     re.[nr] as [group], re.[nr], re.clientID
   from re
   where re.[nr]=1

   UNION ALL

   select r.id, r.amount, 
     CASE WHEN r.amount+re2.amount_total >33
       OR r.clientID<>re2.clientID
         then r.amount ELSE re2.amount_total+r.amount END
       as amount_total,
   CASE WHEN r.amount+re2.amount_total >33 
     OR r.clientID<>re2.clientID THEN 
     r.[nr] ELSE re2.[group] END as [group], r.[nr], r.clientID
   from re r join re2
     on (r.[nr]=re2.[nr]+1 )

  )
  , group_total
  AS
  (
    select [group], clientID, max(amount_total) as total
    FROM re2
    group by [group], clientID
  ),


  result
  as
  (
      select 
   r.id,  r.clientID, r.amount, 
    cast(r.clientid as varchar(20))
      +'-'+char(64+cast(
        dense_rank() 
          over( partition by r.clientID 
               order by r.[clientID], r.[group])
       as varchar(3))) as joinedID
    ,     gt.total as joinedAmount
 from re2 as r join group_total gt
  on (r.clientID=gt.clientID AND r.[group]=gt.[group])
    )
    select * from result