SQL 按一定数量分组
SQL Group By Certain Amounts
我有一个订单 table,其中包含客户 ID 和订单金额。我想加入这些订单,但加入的订单不能超过一定数量。下面是一个例子:
假设最大数量是 33 个托盘,我有一个 table 这样的:
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 个订单 ID,所以我也不希望这是一个缓慢的操作。我正在使用 SQL Server 2014
不确定我是否正确理解了问题,但你可以试试
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 的东西。
我试图通过简单的选择来解决问题,而不是使用显式游标,但那样有点困难。
我已经解决了它并得到了你想要的:
一个TempTable
,一个cursor
,一个counter
用于检查后续金额的总和,CHAR()
生成字母的功能;我计算了这些值并插入到临时 table 中,最后更新了临时 table,以下是我尝试过的 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
您可以在递归 CTE 的帮助下找到建议的解决方案(sql 定义):http://sqlfiddle.com/#!6/285c16/45
基本上 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
我有一个订单 table,其中包含客户 ID 和订单金额。我想加入这些订单,但加入的订单不能超过一定数量。下面是一个例子:
假设最大数量是 33 个托盘,我有一个 table 这样的:
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 个订单 ID,所以我也不希望这是一个缓慢的操作。我正在使用 SQL Server 2014
不确定我是否正确理解了问题,但你可以试试
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 的东西。
我试图通过简单的选择来解决问题,而不是使用显式游标,但那样有点困难。
我已经解决了它并得到了你想要的:
一个TempTable
,一个cursor
,一个counter
用于检查后续金额的总和,CHAR()
生成字母的功能;我计算了这些值并插入到临时 table 中,最后更新了临时 table,以下是我尝试过的 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
您可以在递归 CTE 的帮助下找到建议的解决方案(sql 定义):http://sqlfiddle.com/#!6/285c16/45
基本上 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