我正在研究一个复杂的问题 - Bill Marking for Aging Analysis。数据是这样的:
来源表:
string S, K, generated;
cout << "Enter the message: ";
cin >> S;
cout << "Enter the key: ";
cin >> K;
cout << "The message is: " << S << endl; // output the string
int seed = 0;
for(int i = 0; i < (int) K.length(); i++)
seed += K[i]; // used to generate a certain sequence of numbers
srand(seed); // new seed per new key
cout << "The cipher is: ";
for(int i = 0; i < (int) S.length(); i++) {
int R = rand() % K.length();
char L = 65 + (S[i] - 65 + K[R] - 65) % 26;
cout << L;
generated += L; // to actually have something to use for decryption
}
// FINALLY, to reach to this step and do something like this took me over 2 hours of continuous debugging
cout << endl;
cout << "The message again is: ";
for(int i = 0; i < (int) generated.length(); i++) {
int R = rand() % K.length();
char L = 65 + (generated[i] - 65 + K[R] - 65) % 26;
cout << L;
}
cout << endl;
因此,总借记:13,000和总信用额:11,000和总余额为2,000借方。
因此,目标是根据日期(FIFO)使用相应的借记记录标记所有信用记录,并将每个记录的标记方案存储在单独的表中,如下所示。
目标表具有每条记录的标记详细信息
目标表
TrxnID, Date, CustomerID, DebitCredit, Amount
-----------------------------------------------
D1 01-Apr, RAS12, D, 2000
D2 01-Apr, RAS12, D, 3000
C3 02-Apr, RAS12, C, 4000
D4 03-Apr, RAS12, D, 5000
C5 04-Apr, RAS12, C, 1000
C6 10-Apr, RAS12, C, 6000
D7 25-Apr, RAS12, D, 3000
我一直在使用老式的基于游标的方法,这也变得相当复杂,但相信必须有一套基于机制的机制来处理这个问题。
非常感谢任何帮助。我们正在使用SQL Server 2008 R2。
由于
答案 0 :(得分:0)
我有一个解决方案,但可能需要使用比7个给定行更多的数据进行测试。
首先将源表与自身交叉连接。这将给出债务和信用行的所有组合(7 * 7 = 49)。然后仅选择第一个源表中的debet行和第二个源表中的信用行。我们有4个debet行和3个credit行,因此这会产生12行的输出表。每个债务行将链接到3个信用行。添加每个债务交易ID的计入金额的运行总计(我的演示代码中的列&#34;可用&#34;)。添加一个列,其中包含所有先前债务金额的总和(将所有行的债务金额与较低的债务交易ID相加)。这一栏我命名为#34; UsedBefore&#34;。减去&#34; UsedBefore&#34;来自&#34;可用&#34;给予&#34; Remains&#34;。然后从&#34; Remains&#34;中减去当前的债务金额。给予&#34; RemainsAfter&#34;
使用此表作为第二个选择的输入。现在只选择&#34; Remains&#34;大于0.添加金额为&#34;付费&#34;的列。如果&#34;仍然是&#34;大于债务金额这将是债务金额,否则&#34; Remains&#34;量。添加一个row_number,对每个debet transacton id组中的行进行编号。
使用结果表作为第三个选择的输入。现在只选择row_number等于1或者数量为&#34; RemainsAfter&#34;小于或等于0.您现在拥有所需的行但尚未记入正确的金额。
使用上一个选择的结果作为第四个选择的输入。添加一列&#34; PaidNow&#34;。这是&#34;付费&#34;减去在同一债务交易ID组内支付的先前金额。
试试这个:
select tab3.DebetTrnxID
, tab3.CreditTrnxID
, paid - sum(paid) over (partition by debetTrnxID order by rn) + paid as PaidNow
from (
select * from (
select *
, row_number() over (partition by tab.DebetTrnxID order by tab.CreditDate, tab.CreditTrnxID) as rn
, case when remains >= debetamount then
DebetAmount
else
Remains
end as Paid
from (
select a.TrnxID as DebetTrnxID
, a.Date as DebetDate
, a.DebitCredit as DebetDebetCredit
, a.Amount as DebetAmount
, b.TrnxID as CreditTrnxID
, b.Date as CreditDate
, b.DebitCredit as CreditDebetCredit
, b.Amount as CreditAmount
, sum(b.amount) over (partition by a.trnxid order by a.Date, a.TrnxId, b.Date, b.TrnxID) as Available
, isnull((select sum(c.amount) from source c where c.DebitCredit='D' and c.TrnxID<a.TrnxID),0) as UsedBefore
, sum(b.amount) over (partition by a.trnxid order by a.Date, a.TrnxId, b.Date, b.TrnxID)
- isnull((select sum(c.amount) from source c where c.DebitCredit='D' and c.TrnxID<a.TrnxID),0) as Remains
, sum(b.amount) over (partition by a.trnxid order by a.Date, a.TrnxId, b.Date, b.TrnxID)
- isnull((select sum(c.amount) from source c where c.DebitCredit='D' and c.TrnxID<a.TrnxID),0)
- a.amount as RemainsAfter
from source a
cross join source b
where a.DebitCredit='D'
and b.DebitCredit='C'
) tab
where remains>0
) tab2
where tab2.rn=1
or tab2.RemainsAfter<=0
) tab3
order by tab3.DebetTrnxID
, tab3.CreditTrnxID
阿尔伯特
答案 1 :(得分:0)
我只能使用游标解决问题。以下是以下步骤:
创建一个带有借方和贷方总和的光标,按客户分组 - 这样就会给我一个客户列表,为其调整运行
启动循环
为Debit&amp;创建单独的游标。该客户的信用个人记录
按顺序运行循环,输入正确的逻辑并确保借记和贷记记录正确匹配 - 任何匹配的记录都会插入到单独的表中。这是一个逻辑流程问题,然后是技术问题,以确保只有所需的光标(借方或贷方或两者)移动到下一条记录,只有匹配的余额。
对于超过600,000条记录,整个过程在不到4分钟的时间内完成,这是非常可接受的
结论:有时基于光标的操作比基于操作的操作更快
--step 1
select cv.customercode,sum(case when cv.drcr='D' then cv.Amount else 0
end) as PendAmountDr,
sum(case when cv.drcr='C' then cv.Amount else 0 end) as PendAmountCr
From SourceTable cv
Group by cv.CustomerCode
having sum(case when cv.drcr='D' then cv.Amount else 0 end) <> 0
and sum(case when cv.drcr='C' then cv.Amount else 0 end) <> 0
--Use Fetch Commands & While Loop
--STEP 2 : Create Cursor for Debit Records for that customer order by date
While @@Fetch_Status = 0
DECLARE CVDR CURSOR READ_ONLY FORWARD_ONLY STATIC FOR
select TRANSACTIONID, cv.Amount
From Cashvchr cv
where cv.CompanyID=@CompanyID and isnull(cv.deleted,0) = 0
AND isnull(cv.CustomerCode,'') = @Cust
AND CV.DRCR = 'D'
ORDER BY VCHRDATE,TRANSACTIONID
--STEP 3 : Create Cursor for Credit Records for that customer order by date
DECLARE CVCR CURSOR READ_ONLY FORWARD_ONLY STATIC FOR
select TRANSACTIONID, cv.Amount
From Cashvchr cv
where cv.CompanyID=@CompanyID and isnull(cv.deleted,0) = 0
AND isnull(cv.CustomerCode,'') = @Cust
AND CV.DRCR = 'C'
ORDER BY VCHRDATE,TRANSACTIONID
set @Balamtdr = @amtdr
set @balamtcr = @amtcr
WHILE @AMTTOMARK > 0
BEGIN
SET @CVID = @CVIDDR
SET @MCVID = @CVIDCR
SET @CURRMARKAMT=0
set @skipdr = 0
set @skipcr = 0
IF @BALAMTDR > @BALAMTCR -- i.e. balance debit amount to be marked is bigger from bal.credit amt THEN skip CREDIT
begin
SET @CURRMARKAMT = @balAMTCR
set @skipCr = 1
set @balamtdr = @balamtdr - @balamtcr
end
ELSE IF @balAMTDR < @balAMTCR -- i.e. balance Credit amount is bigger THEN skip Debit
begin
SET @CURRMARKAMT = @balAMTDR
set @skipdr = 1
set @balamtcr = @balamtCr - @balamtdr
end
ELSE -- i.e. balance Credit & Debit amount is same, mark and skip both
begin
SET @CURRMARKAMT = @balamtdr
set @balamtcr = @balamtCr - @balamtdr
set @skipdr = 1
set @skipcr = 1
end
----
INSERT INTO TargetTable
(CASHVCHRID,AMOUNT,MARKEDCASHVCHRID) VALUES
(@CVIDDR,@CURRMARKAMT,@CVIDCR)
set @amttomark = @amttomark - @currmarkamt
if @skipdr = 1 and @amttomark > 0
begin
FETCH next from CVDR INTO @CVIDDR, @AMTDR
if @@fetch_status <> 0
begin
set @amttomark = 0 -- no further marking possible some problem, should exit
end
else
begin
set @balamtdr = @amtdr
end
-- end if
end
-- end if @skipdr = 1
if @skipcr = 1 and @amttomark > 0
begin
FETCH next from CVCR INTO @CVIDCR, @AMTCR
if @@fetch_status <> 0
begin
set @amttomark = 0 -- no further marking possible some problem, should exit
end
else
begin
set @balamtcr = @amtcr
end
-- end if
end
-- end if @skipcr = 1
END
-- END WHILE @AMTTOMARK > 0 FOR DR/CR skip FOR ONE CUSTOMER