【发布时间】:2011-08-14 03:14:24
【问题描述】:
我正在尝试编写一个查询,该查询将通过一个表并将帐户上的任何信用应用于最旧的余额。如果不使用游标,我想不出办法来做到这一点,而且我知道如果可能的话,应该不惜一切代价避免使用游标,所以我来这里寻求帮助。
select * into #balances from [IDAT_AR_BALANCES] where amount > 0
select * into #credits from [IDAT_AR_BALANCES] where amount < 0
create index ba_ID on #balances (CLIENT_ID)
create index cr_ID on #credits (CLIENT_ID)
declare credit_cursor cursor for
select [CLIENT_ID], amount, cvtGUID from #credits
open credit_cursor
declare @client_id varchar(11)
declare @credit money
declare @balance money
declare @cvtGuidBalance uniqueidentifier
declare @cvtGuidCredit uniqueidentifier
fetch next from credit_cursor into @client_id, @credit, @cvtGuidCredit
while @@fetch_status = 0
begin
while(@credit < 0 and (select count(*) from #balances where @client_id = CLIENT_ID and amount <> 0) > 0)
begin
select top 1 @balance = amount, @cvtGuidBalance = cvtGuid from #balances where @client_id = CLIENT_ID and amount <> 0 order by AGING_DATE
set @credit = @balance + @credit
if(@credit > 0)
begin
update #balances set amount = @credit where cvtGuid = @cvtGuidBalance
set @credit = 0
end
else
begin
update #balances set amount = 0 where cvtGuid = @cvtGuidBalance
end
end
update #credits set amount = @credit where cvtGuid = @cvtGuidCredit
fetch next from credit_cursor into @client_id, @credit, @cvtGuidCredit
end
close credit_cursor
deallocate credit_cursor
delete #balances where AMOUNT = 0
delete #credits where AMOUNT = 0
truncate table [IDAT_AR_BALANCES]
insert [IDAT_AR_BALANCES] select * from #balances
insert [IDAT_AR_BALANCES] select * from #credits
drop table #balances
drop table #credits
在我的 10000 条记录和 1000 个客户端的测试用例中,运行需要 26 秒,通过添加 CLIENT_ID 上的两个索引,我能够将数字降低到 14 秒。但是这对于我的需要来说仍然太慢了,最终结果可能有多达 10000 个客户端和超过 4,000,000 条记录,因此运行时间很容易变成两位数分钟。
任何关于如何重组它以删除光标的建议将不胜感激。
示例(更新后表明您可以在运行后获得多个学分):
before
cvtGuid client_id ammount AGING_DATE
xxxxxx 1 20.00 1/1/2011
xxxxxx 1 30.00 1/2/2011
xxxxxx 1 -10.00 1/3/2011
xxxxxx 1 5.00 1/4/2011
xxxxxx 2 20.00 1/1/2011
xxxxxx 2 15.00 1/2/2011
xxxxxx 2 -40.00 1/3/2011
xxxxxx 2 5.00 1/4/2011
xxxxxx 3 10.00 1/1/2011
xxxxxx 3 -20.00 1/2/2011
xxxxxx 3 5.00 1/3/2011
xxxxxx 3 -8.00 1/4/2011
after
cvtGuid client_id ammount AGING_DATE
xxxxxx 1 10.00 1/1/2011
xxxxxx 1 30.00 1/2/2011
xxxxxx 1 5.00 1/4/2011
xxxxxx 3 -5.00 1/2/2011
xxxxxx 3 -8.00 1/4/2011
所以它会将负信用应用于最旧的正余额(示例中的客户端 1),如果在完成后没有剩余的正余额,它会留下剩余的负余额(客户端 3),如果他们完全取消(90% 的时间都是真实数据的情况),它将完全删除记录(客户端 2)。
【问题讨论】:
-
您能否在数据之前/之后举一些例子,这样我们就不必在我们回答之前先尝试弄清楚您的光标在做什么?
-
同一个
client_id是否可能有多个负余额? -
@ypercube 是的,可能不止一个。
-
@Scott - client_id 3 的日期不应该是 2011 年 1 月 3 日还是 2011 年 1 月 2 日?如果是 2011 年 1 月 2 日,我不明白这个理由。
-
@Lieven 这是因为信用额度的日期是 2011 年 1 月 2 日,并且只有 20 次中的 15 次被使用,所以您的账户上还有 2011 年 1 月 2 日起的 5 美元信用额度.
标签: sql sql-server-2005 optimization