Я пишу хранимую процедуру, которая берет таблицу авуаров и назначает их в таблицу сегментов, если они являются частью одного и того же аккаунта. Если у холдинга та же учетная запись, что и у ведра, я заполняю ведро суммой каждого удержания по этой учетной записи до тех пор, пока а) ведро не заполнится, и в этом случае перейдем к следующему ведру; б) больше не будет владений оставить для этой учетной записи, и в этом случае перейти к следующей учетной записи
Существует три сценария для владений и групп одного и того же счета:
Запасы заполняют ведра равномерно - это сумма всех запасов = сумма всех ведер
Запасы не заполняют все сегменты - в этом случае переходите к следующему аккаунту и игнорируйте оставшиеся сегменты для предыдущего аккаунта
Запасы переполняют сегменты - в этом сценарии мы игнорируем оставшиеся запасы
Каждое владение должно быть помечено, какому ведру он назначен и сколько применяется к каждому ведру. Пример с некоторыми комментариями ниже:
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 должен показывать 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
Сценарий, в котором ведро недостаточно заполнено, заставляет мой скрипт работать неправильно. Я надеюсь, что кто-то сможет указать, где я иду не так, - я чувствую, как будто он слишком силен здесь. Кроме того, я считаю, что это может быть связано с выбором, а не серией обновлений. Спасибо за любую помощь, спасибо.