我正在编写一个存储过程,该过程将拥有一个表并将其分配到存储桶表(如果它们是同一帐户的一部分)。如果某个库存与该存储桶具有相同的帐户,那么我将用该帐户下的每个库存的数量填充该存储桶,直到a)该存储桶被填充,在这种情况下,移至下一个存储桶b)没有更多的存储留给该帐户,在这种情况下,请转到下一个帐户
对于同一帐户的库存和存储桶,存在三种情况:
保有物均匀地填满了桶-那是所有保有物的总和=所有保管物的总和
这些资产并没有填满所有的存储桶-在这种情况下,请转到下一个帐户,而忽略上一个帐户的剩余存储桶
所有馆藏中的馆藏过多-在这种情况下,我们将忽略其余馆藏
每个馆藏都必须标记为其分配给哪个存储区以及对每个存储区应用多少存储区。下面是带有一些注释的示例:
Buckets
----------------------------------------
Bucket BucketAccount TotalAmount
1 GB111 30
2 GB111 50
3 GB222 100
4 GB333 150
Holdings (before execution)
------------------------------------------------------------------------------
ID Account Amount Bucket AmountApplied
1 GB111 50 null null
2 GB111 40 null null
3 GB222 30 null null
4 GB222 40 null null
5 GB333 5 null null
6 GB333 145 null null
7 GB333 50 null null
If(OBJECT_ID('tempdb..#buckets') Is Not Null)
Begin
Drop Table #buckets
End
CREATE TABLE #buckets
(
Bucket int,
BucketAccount nvarchar(10),
TotalAmount Decimal
);
insert into #buckets values
(1, 'GB111', 30),
(2, 'GB111', 50),
(3, 'GB222', 100),
(4, 'GB333', 150)
If(OBJECT_ID('tempdb..#holdings') Is Not Null)
Begin
Drop Table #holdings
End
CREATE TABLE #holdings
(
ID int,
Account nvarchar(10),
Amount decimal,
Bucket int null,
TotalAmount decimal null
);
insert into #holdings (ID, Account, Amount, Bucket, TotalAmount)
values
(1, 'GB111', 50, null, null),
(2, 'GB111', 40, null, null),
(3, 'GB222', 30, null, null),
(4, 'GB222', 40, null, null),
(5, 'GB333', 5, null, null),
(6, 'GB333', 145, null, null),
(7, 'GB333', 50, null, null)
select *
from
(select
hold.Account, maxIds.ID as SubTotalId, sum(hold.Amount) as PartAmount
from #holdings hold
inner join #holdings maxIds
on hold.Account = maxIds.Account
and hold.Id <= maxIds.ID
group by hold.Account, maxIds.Id) partHoldings
right join
(select buckets.BucketAccount, subBuckets.Bucket, sum(buckets.TotalAmount) as PartAmount
from #buckets buckets
inner join #buckets subBuckets
on buckets.BucketAccount = subBuckets.BucketAccount
and buckets.Bucket <= subBuckets.Bucket
group by buckets.BucketAccount, subBuckets.Bucket) partBuckets
on partHoldings.Account = partBuckets.BucketAccount
and partHoldings.PartAmount >= partBuckets.PartAmount
select
-- * ,
BucketAccount, Bucket, ID as holdingId,
case
when MinHoldingCoveringBucket < Id and Id < MaxHoldingCoveringBucket then Amount
when MinHoldingCoveringBucket = Id and Id = MaxHoldingCoveringBucket then PartAmount - prevTotalPartAmount
when MinHoldingCoveringBucket = Id and Id <> MaxHoldingCoveringBucket then holdPartAmount - prevTotalPartAmount
when MinHoldingCoveringBucket <> Id and Id = MaxHoldingCoveringBucket then PartAmount - holdPrevPartAmount
else null
end as AmountApplied
from
(select
holdingsBuckets.BucketAccount, holdingsBuckets.Bucket, holdingsBuckets.PartAmount, holdingsBuckets.prevTotalPartAmount
, IsNull(MinHoldingCoveringBucket, minAccountHoldingId) as MinHoldingCoveringBucket
, IsNull(MaxHoldingCoveringBucket, maxAccountHoldingId) as MaxHoldingCoveringBucket
, hold.ID, hold.Amount
, partHoldings.PartAmount as holdPartAmount
, partHoldings.prevPartAmount as holdPrevPartAmount
from
(select
topLimits.*
, min(botLimits.SubTotalId) as MinHoldingCoveringBucket
from
(select
partBuckets.*
, min(partHoldings.SubTotalId) as MaxHoldingCoveringBucket
from
(select subBuckets.BucketAccount, subBuckets.Bucket, sum(buckets.TotalAmount) as PartAmount, sum(buckets.TotalAmount) - subBuckets.TotalAmount as prevTotalPartAmount
from #buckets buckets
inner join #buckets subBuckets
on buckets.BucketAccount = subBuckets.BucketAccount
and buckets.Bucket <= subBuckets.Bucket
group by subBuckets.BucketAccount, subBuckets.Bucket, subBuckets.TotalAmount) partBuckets
left join
(select
hold.Account, maxIds.ID as SubTotalId, sum(hold.Amount) as PartAmount
from #holdings hold
inner join #holdings maxIds
on hold.Account = maxIds.Account
and hold.Id <= maxIds.ID
group by hold.Account, maxIds.Id) partHoldings
on partHoldings.Account = partBuckets.BucketAccount
and partHoldings.PartAmount >= partBuckets.PartAmount
left join
(select
hold.Account, maxIds.ID as SubTotalId, sum(hold.Amount) as PartAmount
from #holdings hold
inner join #holdings maxIds
on hold.Account = maxIds.Account
and hold.Id <= maxIds.ID
group by hold.Account, maxIds.Id) partHoldings2
on partBuckets.BucketAccount = partHoldings2.Account
and partHoldings.SubTotalId >= partHoldings2.SubTotalId
and partHoldings2.PartAmount > partBuckets.prevTotalPartAmount
group by partBuckets.BucketAccount, partBuckets.Bucket, partBuckets.PartAmount, partBuckets.prevTotalPartAmount) topLimits
left join
(select
hold.Account, maxIds.ID as SubTotalId, sum(hold.Amount) as PartAmount
from #holdings hold
inner join #holdings maxIds
on hold.Account = maxIds.Account
and hold.Id <= maxIds.ID
group by hold.Account, maxIds.Id) botLimits
on topLimits.BucketAccount = botLimits.Account
and botLimits.PartAmount > topLimits.prevTotalPartAmount
and botLimits.SubTotalId < topLimits.MaxHoldingCoveringBucket
group by topLimits.BucketAccount, topLimits.Bucket, topLimits.PartAmount, topLimits.prevTotalPartAmount, topLimits.MaxHoldingCoveringBucket) holdingsBuckets
inner join
(select Account, min(Id) as minAccountHoldingId, max(id) as maxAccountHoldingId
from #holdings
group by Account) edgeAccountHoldings
on holdingsBuckets.BucketAccount = edgeAccountHoldings.Account
right join #holdings hold
on holdingsBuckets.BucketAccount = hold.Account
and IsNull(MinHoldingCoveringBucket, minAccountHoldingId) <= hold.ID
and hold.ID <= IsNull(MaxHoldingCoveringBucket, maxAccountHoldingId)
left join
(
select
hold.Account, maxIds.ID as SubTotalId, sum(hold.Amount) as PartAmount, sum(hold.Amount) - maxIds.Amount as prevPartAmount
from #holdings hold
inner join #holdings maxIds
on hold.Account = maxIds.Account
and hold.Id <= maxIds.ID
group by hold.Account, maxIds.Id, maxIds.Amount
) partHoldings
on partHoldings.Account = holdingsBuckets.BucketAccount
and hold.ID = partHoldings.SubTotalId) selectionData
执行后:
HoldingId 5应该在amountApplied字段中显示40,而不是70。我们从第一个保留中应用了30,然后从第二个保留中应用了40,将其求和到70。
Holdings
--------------------------------------------------------------------------------------------------------------------------------------------
ID Account Amount Bucket AmountApplied Comments
1 GB111 50 1 30 Applied 30. Bucket 1 is filled with 20 leftover, move to next bucket of same account
2 GB111 20 2 20 Insert new record. Applied 20 (from leftover in Bucket 1), and there is 30 leftover to cover in Bucket 2
3 GB111 40 2 30 Applied 30, 10 leftover in Bucket 2. We are out of holdings for this account, move on to next account
4 GB222 30 3 30 Applied 30, 70 leftover in Bucket 3
5 GB222 40 3 **70** Applied 40, 30 leftover in bucket 3. Bucket is not filled and we are out of holdings for this account. Move on to next account
6 GB333 5 4 5 Applied 5, 145 leftover in Bucket 4
7 GB333 145 4 145 Applied 145, Bucket 4 is filled with 0 leftover, move on to next account
8 GB333 50 null null Skip as Bucket 4 is already filled
存储桶填充不足的情况导致我的脚本无法正常工作。我希望有人能够指出我要去的地方-我觉得这里的工程似乎过分设计。另外,我认为这样做可能是导致选择的原因,而不是一系列更新。感谢您的任何帮助,谢谢。
答案 0 :(得分:1)
这很有趣,我不确定100%负持有该怎么做-假设它会增加存储桶中的可用空间?
无论如何,如果上述条件成立,这将为您完成:
;with row1 as
(
select ID, Account, Amount, b.Bucket, b.TotalAmount,
row_number() over (partition by Account order by ID, b.Bucket) rn
from #holdings h
join #buckets b on b.BucketAccount=h.Account
)
, allocations as
(
select ID, Account, Amount, Bucket, TotalAmount,
convert(decimal,case when Amount<=TotalAmount then Amount else TotalAmount end) as Allocated,
convert(decimal,case when Amount>=TotalAmount then Amount-TotalAmount else 0.0 end) as HoldingRemaining,
convert(decimal,case when Amount>=TotalAmount then 0.0 else TotalAmount-Amount end) as BucketRemaining
from row1 where rn=1
union all
select ID, Account, Amount, Bucket, TotalAmount,
convert(decimal,case when HoldingRemaining<=BucketRemaining then HoldingRemaining else BucketRemaining end) as Allocated,
convert(decimal,case when HoldingRemaining>=BucketRemaining then HoldingRemaining-BucketRemaining else 0.0 end) as HoldingRemaining,
convert(decimal,case when HoldingRemaining>=BucketRemaining then 0.0 else BucketRemaining-HoldingRemaining end) as BucketRemaining
from (
select h.ID, h.Account, h.Amount, b.Bucket, b.TotalAmount,
case when h.ID=a.ID then HoldingRemaining else h.Amount end as HoldingRemaining,
case when h.Bucket=a.Bucket then BucketRemaining else b.TotalAmount end as BucketRemaining
from allocations a
-- Move to next holding if required
join #holdings h on h.Account=a.Account
and (
(HoldingRemaining>0 and h.ID=a.ID)
or (HoldingRemaining=0 and h.ID=a.ID+1)
)
-- Move to next bucket if required
join #buckets b on b.BucketAccount=a.Account
and (
(BucketRemaining>0 and b.Bucket=a.Bucket)
or (BucketRemaining=0 and b.Bucket=a.Bucket+1)
)
) q
)
select * from allocations order by Account, ID, Bucket
结果:
ID Account Amount Bucket TotalAmount Allocated HoldingRemaining BucketRemaining
1 GB111 50 1 30 30 20 0
1 GB111 50 2 50 20 0 30
2 GB111 40 2 50 40 0 10
3 GB222 30 3 100 30 0 70
4 GB222 40 3 100 40 0 60
5 GB333 -100 4 150 -100 0 250
6 GB333 250 4 150 150 100 0
答案 1 :(得分:1)
这是我使用的DDL:
create table Buckets (Bucket int, BucketAccount varchar(5), TotalAmount int);
insert into Buckets (Bucket, BucketAccount, TotalAmount) values
(1, 'GB111', 30), (2, 'GB111', 50), (3, 'GB222', 100), (4, 'GB333', 150),
(5, 'GB444', 20), (6, 'GB444', 20), (7, 'GB444', 20);
create table Holdings (ID int, Account varchar(5), Amount int);
insert into Holdings (ID, Account, Amount) values
(1, 'GB111', 50), (2, 'GB111', 40), (3, 'GB222', 30),
(4, 'GB222', 40), (5, 'GB333', 100), (6, 'GB333', 250), (7, 'GB333', 50),
(8, 'GB444', 15), (9, 'GB444', 30), (10, 'GB444', 10);
GB444
的情况是单个馆藏跨越三个不同的存储桶,但您提供的样本数据并未表示这种情况。请注意,我还编辑了查询以正确处理这种情况。
with b as (
select *,
sum(TotalAmount) over (partition by BucketAccount order by Bucket) -
TotalAmount as e,
sum(TotalAmount) over (partition by BucketAccount order by Bucket) as f,
sum(TotalAmount) over (partition by BucketAccount) as AccountSize
from Buckets
), h as (
select *,
sum(Amount) over (partition by Account order by ID) - Amount as a,
sum(Amount) over (partition by Account order by ID) as b
from Holdings
)
select
h.Account, b.AccountSize,
h.ID, h.Amount, b.Bucket, b.TotalAmount as BucketSize,
case
when h.a >= b.e and h.b <= b.f then h.Amount
when h.a < b.e and h.b <= b.f then h.b - b.e
else b.f - case when h.a > b.e then h.a else b.e end
end as AmountApplied,
case
when h.a >= b.e and h.b <= b.f then 'Type 1: ' +
cast(b.f - h.b as char(3)) + ' unfilled'
when h.a < b.e and h.b <= b.f then 'Type 2: ' +
cast(b.f - h.b as char(3)) + ' unfilled'
else case when h.a > b.e then 'Type 3: ' else 'Type 4: ' end +
cast(h.b - b.f as char(3)) + ' overflows'
end as Scenario,
case when h.a >= b.e and h.b <= b.f then 'No' else 'Yes' end as Spans,
case when h.b >= b.f then 'Yes' else 'No' end as Depletes
from h inner join b on b.BucketAccount = h.Account and h.a < b.f and h.b > b.e
order by h.Account, h.ID, b.Bucket;
这将以累积方式预计算保管箱和铲斗的位置,就像沿着数字线堆叠一样。然后根据询问每个保留范围是否与每个存储桶范围重叠的方式进行联接。
输出中包括其他可能有用的列,这些列描述了如何将货品的每个部分应用于存储桶。
也
为了娱乐,我played around通过一点可视化的方式。希望对您有所帮助。