将嵌套游标转换为基于集合的方法?
Converting nested cursor into a set based approach?
所以我终于完成了查询的更新代码
(我无法让更新游标工作,所以我只是将所有变量插入到不同的 table 中,然后使用 join + coalesce 来更正数字。“for update”语句仍在游标声明中,因为采用即使我删除了所有更新语句,它们也会破坏光标)
但是当我尝试复制并粘贴到 SSRS 时,所有用于游标的变量都被视为“未声明”
The variable name '@TranLoan' has already been declared. Variable names must be unique within a query batch or stored procedure.
Must declare the scalar variable "@TranPrin".
Must declare the scalar variable "@DelqPrin".
[每次每个变量出现在代码中时,相同的变量都会继续一次,并且两个游标的所有变量都在列表中,但出现相同的错误]
问题是@TranLoan 只声明了一次。附件是代码的简化版本(基本上每笔贷款只做 1 笔而不是 3 笔)
如果有人能想出一种基于集合的方法,我会很高兴,这样我就可以完全抛弃这些游标了。
Table 设置
IF OBJECT_ID('tempdb.dbo.#DQ') IS NOT NULL DROP TABLE #DQ
IF OBJECT_ID('tempdb.dbo.#DelqAmts') IS NOT NULL DROP TABLE #DelqAmts
IF OBJECT_ID('tempdb.dbo.#Tran') IS NOT NULL DROP TABLE #Tran
--IF OBJECT_ID('tempdb.dbo.#DelqAmts') IS NOT NULL DROP TABLE #DelqAmts
CREATE TABLE #DQ
([LoanNumber] int, [Amount] money, [PaidToDate] datetime);
CREATE TABLE #DelqAmts
([LoanNumber] int, [Amount] money, [PaidToDate] datetime);
INSERT INTO #DQ
([LoanNumber], [Amount], [PaidToDate])
VALUES
(56452, 739.97, '2015-09-01 00:00:00'),
(56452, 738.35, '2015-10-01 00:00:00'),
(56452, 736.72, '2015-11-01 00:00:00'),
(56452, 735.08, '2015-12-01 00:00:00'),
(56452, 733.44, '2016-01-01 00:00:00'),
(56452, 731.79, '2016-02-01 00:00:00'),
(56452, 730.13, '2016-03-01 00:00:00'),
(56452, 728.46, '2016-04-01 00:00:00'),
(56452, 726.79, '2016-05-01 00:00:00'),
(56452, 725.1, '2016-06-01 00:00:00'),
(78553, 436.43, '2016-02-01 00:00:00'),
(78553, 435.72, '2016-03-01 00:00:00'),
(78553, 435, '2016-04-01 00:00:00'),
(78553, 434.28, '2016-05-01 00:00:00'),
(78553, 433.55, '2016-06-01 00:00:00');
CREATE TABLE #Tran
([LoanNumber] int, [TranAmount] money);
INSERT INTO #Tran
(LoanNumber, TranAmount)
VALUES
(56452, 833.97),
(78553, 1653.17);
table 设置结束
declare @Col money = 0 -- Total Collected principal
, @TranLoan varchar(10) --Loan number for current transaction
, @TranPrin money = 0 -- Tran loan collected principal
, @POPrin money = 0 -- Tran loan collected principal
, @TranCursor as CURSOR
, @DelqLoan varchar(10)
, @DelqPrin money -- Current loan collected principal
, @DelqPTD date
, @DelqCursor as CURSOR
set @TranCursor = Cursor FORWARD_ONLY
For Select LoanNumber, [TranAmount]
From #Tran
Open @TranCursor;
Fetch next from @TranCursor into @TranLoan, @TranPrin
set @Col = 0
while (@@Fetch_status = 0)
Begin
select @TranLoan as Loan, @TranPrin as TPrin
set @DelqCursor = CURSOR FORWARD_ONLY
FOR select LoanNumber,Amount ,PaidToDate
from #DQ
where LoanNumber = @TranLoan
Order by PaidToDate Asc;
-- For update OF Amount;
Open @DelqCursor
Fetch next from @DelqCursor into @DelqLoan, @DelqPrin,@DelqPTD
while (@@Fetch_status = 0)
Begin
IF @TranPrin = 0
SET @DelqPrin = 0
End
IF @TranPrin > 0 AND @TranPrin < @DelqPrin
BEGIN
SET @Col = @Col + @TranPrin
SET @DelqPrin = @TranPrin
-- select @TranLoan as Loan, @TranPrin as TPrin, @DelqPrin as DPrin, @DelqPTD as PTD, @Col as Col
SET @TranPrin = 0
END
IF @TranPrin >0 AND @TranPrin > @DelqPrin
BEGIN
SET @TranPrin = @TranPrin - @DelqPrin
SET @Col = @Col + @DelqPrin
END
Insert into #DelqAmts values (@DelqLoan, @DelqPrin, @DelqPTD)
Fetch next from @DelqCursor into @DelqLoan, @DelqPrin,@DelqPTD
End
Close @DelqCursor-- Finished with delinquent data for this loan. We close the cursor
Fetch next from @TranCursor into @TranLoan, @TranPrin
End
Close @TranCursor
deallocate @DelqCursor
deallocate @TranCursor
select * from #delqAmts -- This is a simplification of what I need out of this code block and isn't the end result of the report
此后不再使用变量,因此不包括其余代码。
因此,在查看了 over 的文档以及如何进行滚动求和之后,这变得容易多了。这是最终查询(减去 table 设置)
select LoanNumber, PaidToDate, PrincipalCollected,
SumPrin, DelqPrin,
case when SumPrin <= PrincipalCollected then DelqPrin
When PrincipalCollected between SumPrin-DelqPrin and SumPrin then PrincipalCollected- (SumPrin-DelqPrin)
when SumPrin > PrincipalCollected then 0
end as ColPrin,
InterestCollected, SumInt, DelqInt,
case when SumInt <= InterestCollected then DelqInt
When InterestCollected between SumInt-DelqInt and SumInt then InterestCollected- (SumInt-DelqInt)
when SumInt > InterestCollected then 0
end as ColInt,
ServiceFee, SumServ, DelqServFee,
case when SumServ <= ServiceFee then DelqServFee
When ServiceFee between SumServ-DelqServFee and SumServ then ServiceFee- (SumServ-DelqServFee)
when SumServ > ServiceFee then 0
end as ColServ
from (
SELECT tf.LoanNumber,tf.PrincipalCollected,InterestCollected, ServiceFee,
dq.PaidToDate,
DelqPrin, SUM(DelqPrin) OVER (Partition by dq.LoanNumber ORDER BY dq.PaidToDate) as SumPrin,
DelqInt, SUM(DelqInt) OVER (Partition by dq.LoanNumber ORDER BY dq.PaidToDate) as SumInt,
DelqServFee, SUM(DelqServFee) OVER (Partition by dq.LoanNumber ORDER BY dq.PaidToDate) as SumServ
FROM #tran tf
left join #dq dq on tf.LoanNumber = dq.LoanNumber
) as A
ORDER BY LOanNumber, PaidToDate
作为基于集合的方法要容易得多。只需要将滚动总和作为子查询,然后在之后应用公式来获取我需要的数字。
所以我终于完成了查询的更新代码
但是当我尝试复制并粘贴到 SSRS 时,所有用于游标的变量都被视为“未声明”
The variable name '@TranLoan' has already been declared. Variable names must be unique within a query batch or stored procedure.
Must declare the scalar variable "@TranPrin".
Must declare the scalar variable "@DelqPrin".
[每次每个变量出现在代码中时,相同的变量都会继续一次,并且两个游标的所有变量都在列表中,但出现相同的错误] 问题是@TranLoan 只声明了一次。附件是代码的简化版本(基本上每笔贷款只做 1 笔而不是 3 笔) 如果有人能想出一种基于集合的方法,我会很高兴,这样我就可以完全抛弃这些游标了。
Table 设置
IF OBJECT_ID('tempdb.dbo.#DQ') IS NOT NULL DROP TABLE #DQ
IF OBJECT_ID('tempdb.dbo.#DelqAmts') IS NOT NULL DROP TABLE #DelqAmts
IF OBJECT_ID('tempdb.dbo.#Tran') IS NOT NULL DROP TABLE #Tran
--IF OBJECT_ID('tempdb.dbo.#DelqAmts') IS NOT NULL DROP TABLE #DelqAmts
CREATE TABLE #DQ
([LoanNumber] int, [Amount] money, [PaidToDate] datetime);
CREATE TABLE #DelqAmts
([LoanNumber] int, [Amount] money, [PaidToDate] datetime);
INSERT INTO #DQ
([LoanNumber], [Amount], [PaidToDate])
VALUES
(56452, 739.97, '2015-09-01 00:00:00'),
(56452, 738.35, '2015-10-01 00:00:00'),
(56452, 736.72, '2015-11-01 00:00:00'),
(56452, 735.08, '2015-12-01 00:00:00'),
(56452, 733.44, '2016-01-01 00:00:00'),
(56452, 731.79, '2016-02-01 00:00:00'),
(56452, 730.13, '2016-03-01 00:00:00'),
(56452, 728.46, '2016-04-01 00:00:00'),
(56452, 726.79, '2016-05-01 00:00:00'),
(56452, 725.1, '2016-06-01 00:00:00'),
(78553, 436.43, '2016-02-01 00:00:00'),
(78553, 435.72, '2016-03-01 00:00:00'),
(78553, 435, '2016-04-01 00:00:00'),
(78553, 434.28, '2016-05-01 00:00:00'),
(78553, 433.55, '2016-06-01 00:00:00');
CREATE TABLE #Tran
([LoanNumber] int, [TranAmount] money);
INSERT INTO #Tran
(LoanNumber, TranAmount)
VALUES
(56452, 833.97),
(78553, 1653.17);
table 设置结束
declare @Col money = 0 -- Total Collected principal
, @TranLoan varchar(10) --Loan number for current transaction
, @TranPrin money = 0 -- Tran loan collected principal
, @POPrin money = 0 -- Tran loan collected principal
, @TranCursor as CURSOR
, @DelqLoan varchar(10)
, @DelqPrin money -- Current loan collected principal
, @DelqPTD date
, @DelqCursor as CURSOR
set @TranCursor = Cursor FORWARD_ONLY
For Select LoanNumber, [TranAmount]
From #Tran
Open @TranCursor;
Fetch next from @TranCursor into @TranLoan, @TranPrin
set @Col = 0
while (@@Fetch_status = 0)
Begin
select @TranLoan as Loan, @TranPrin as TPrin
set @DelqCursor = CURSOR FORWARD_ONLY
FOR select LoanNumber,Amount ,PaidToDate
from #DQ
where LoanNumber = @TranLoan
Order by PaidToDate Asc;
-- For update OF Amount;
Open @DelqCursor
Fetch next from @DelqCursor into @DelqLoan, @DelqPrin,@DelqPTD
while (@@Fetch_status = 0)
Begin
IF @TranPrin = 0
SET @DelqPrin = 0
End
IF @TranPrin > 0 AND @TranPrin < @DelqPrin
BEGIN
SET @Col = @Col + @TranPrin
SET @DelqPrin = @TranPrin
-- select @TranLoan as Loan, @TranPrin as TPrin, @DelqPrin as DPrin, @DelqPTD as PTD, @Col as Col
SET @TranPrin = 0
END
IF @TranPrin >0 AND @TranPrin > @DelqPrin
BEGIN
SET @TranPrin = @TranPrin - @DelqPrin
SET @Col = @Col + @DelqPrin
END
Insert into #DelqAmts values (@DelqLoan, @DelqPrin, @DelqPTD)
Fetch next from @DelqCursor into @DelqLoan, @DelqPrin,@DelqPTD
End
Close @DelqCursor-- Finished with delinquent data for this loan. We close the cursor
Fetch next from @TranCursor into @TranLoan, @TranPrin
End
Close @TranCursor
deallocate @DelqCursor
deallocate @TranCursor
select * from #delqAmts -- This is a simplification of what I need out of this code block and isn't the end result of the report
此后不再使用变量,因此不包括其余代码。
因此,在查看了 over 的文档以及如何进行滚动求和之后,这变得容易多了。这是最终查询(减去 table 设置)
select LoanNumber, PaidToDate, PrincipalCollected,
SumPrin, DelqPrin,
case when SumPrin <= PrincipalCollected then DelqPrin
When PrincipalCollected between SumPrin-DelqPrin and SumPrin then PrincipalCollected- (SumPrin-DelqPrin)
when SumPrin > PrincipalCollected then 0
end as ColPrin,
InterestCollected, SumInt, DelqInt,
case when SumInt <= InterestCollected then DelqInt
When InterestCollected between SumInt-DelqInt and SumInt then InterestCollected- (SumInt-DelqInt)
when SumInt > InterestCollected then 0
end as ColInt,
ServiceFee, SumServ, DelqServFee,
case when SumServ <= ServiceFee then DelqServFee
When ServiceFee between SumServ-DelqServFee and SumServ then ServiceFee- (SumServ-DelqServFee)
when SumServ > ServiceFee then 0
end as ColServ
from (
SELECT tf.LoanNumber,tf.PrincipalCollected,InterestCollected, ServiceFee,
dq.PaidToDate,
DelqPrin, SUM(DelqPrin) OVER (Partition by dq.LoanNumber ORDER BY dq.PaidToDate) as SumPrin,
DelqInt, SUM(DelqInt) OVER (Partition by dq.LoanNumber ORDER BY dq.PaidToDate) as SumInt,
DelqServFee, SUM(DelqServFee) OVER (Partition by dq.LoanNumber ORDER BY dq.PaidToDate) as SumServ
FROM #tran tf
left join #dq dq on tf.LoanNumber = dq.LoanNumber
) as A
ORDER BY LOanNumber, PaidToDate
作为基于集合的方法要容易得多。只需要将滚动总和作为子查询,然后在之后应用公式来获取我需要的数字。