SQL 服务器游标与交叉应用
SQL SERVER CURSOR VS CROSS APPLY
我有 BatchDetail table 作为
对于每一行,我都想检查总值是否为负并更新一个标志,以便在进一步处理时不会考虑这些行。我采取了以下方法
获取总值为正的所有行,即 pid 为 1、3、5、6 的行。
使用游标并使用 BatchDetail 逐一检查以找到相应的负总数并更新这些行。
由于 batch_detail table 有上万行,因此这种方法花费了太多时间。因此,在谷歌搜索之后,我决定使用 CROSS APPLY,其中我采用了与 batchDetail table 相同的列的 temp table 加上作为 newPid 的新列,插入的行具有正总数并交叉应用批次详细信息 table 如:
UPDATE TEMP_TABLE
SET TEMP_TABLE.NEWPID=TEMP.PID,TEMP_TABLE.FLAG=1
FROM TEMP_TABLE T CROSS APPLY
(SELECT * FROM BATCH_DETL_TBL
WHERE LOAN_NUMBER=T.LAON_NUMBER AND TOTAL=-T.TOTAL)) TEMP
问题是,一旦 pid 3(总计 50)的行与 pid 4(总计 -50)匹配,pid 4 行不应再次与 pid 5 行匹配。因此,一旦更新了具有正数和负数的行,就不应考虑下一组行。
你可以只使用join
,但是你需要一个序列号来匹配total
:
UPDATE TEMP_TABLE
SET TEMP_TABLE.NEWPID = TEMP.PID,
TEMP_TABLE.FLAG = 1
FROM (SELECT t.*,
row_number() over (partition by total order by (select null)) as seqnum
FROM TEMP_TABLE T
) t JOIN
(SELECT dt.*,
row_number() over (partition by total order by (select null)) as seqnum
FROM BATCH_DETL_TBL dt
) dt
on dt.LOAN_NUMBER = T.LOAN_NUMBER AND
dt.TOTAL = -T.TOTAL AND
dt.seqnum = T.seqnum;
这应该比游标快得多(建议:避免使用游标,除非你真的别无选择)。 cross apply
和 join
的性能应该相似。为了性能,您还可以在 batch_detl_tbl(load_number, total)
.
上添加索引
这是一个棘手的问题。对于每个唯一的正总数,您必须按照该总数对所有记录进行排序,并独立地对具有该总数的 负数 的所有记录进行排序,以便您可以将它们匹配起来。这确保每条记录只匹配一次。
以下是使用您的测试数据完成的方法:
create table t1 (loan_number int, pid int, total int, newPid int, flag int );
insert into t1 (loan_number, pid, total ) values (411001,1,100), (411001,2,-100), (411001,3,50), (411001,4,-50), (411001,5,50), (411001,6,25);
update pos set newPid=neg.pid, flag=1 from
( select loan_number, pid, total,
row_number() over (partition by total order by pid) rn
from t1
where total>0) pos
inner join
(select loan_number, pid, total,
row_number() over (partition by total order by pid) rn
from t1
where total<0) neg
on neg.loan_number = pos.loan_number
and neg.total =- pos.total
and neg.rn = pos.rn
;
我有 BatchDetail table 作为
对于每一行,我都想检查总值是否为负并更新一个标志,以便在进一步处理时不会考虑这些行。我采取了以下方法
UPDATE TEMP_TABLE
SET TEMP_TABLE.NEWPID=TEMP.PID,TEMP_TABLE.FLAG=1
FROM TEMP_TABLE T CROSS APPLY
(SELECT * FROM BATCH_DETL_TBL
WHERE LOAN_NUMBER=T.LAON_NUMBER AND TOTAL=-T.TOTAL)) TEMP
问题是,一旦 pid 3(总计 50)的行与 pid 4(总计 -50)匹配,pid 4 行不应再次与 pid 5 行匹配。因此,一旦更新了具有正数和负数的行,就不应考虑下一组行。
你可以只使用join
,但是你需要一个序列号来匹配total
:
UPDATE TEMP_TABLE
SET TEMP_TABLE.NEWPID = TEMP.PID,
TEMP_TABLE.FLAG = 1
FROM (SELECT t.*,
row_number() over (partition by total order by (select null)) as seqnum
FROM TEMP_TABLE T
) t JOIN
(SELECT dt.*,
row_number() over (partition by total order by (select null)) as seqnum
FROM BATCH_DETL_TBL dt
) dt
on dt.LOAN_NUMBER = T.LOAN_NUMBER AND
dt.TOTAL = -T.TOTAL AND
dt.seqnum = T.seqnum;
这应该比游标快得多(建议:避免使用游标,除非你真的别无选择)。 cross apply
和 join
的性能应该相似。为了性能,您还可以在 batch_detl_tbl(load_number, total)
.
这是一个棘手的问题。对于每个唯一的正总数,您必须按照该总数对所有记录进行排序,并独立地对具有该总数的 负数 的所有记录进行排序,以便您可以将它们匹配起来。这确保每条记录只匹配一次。
以下是使用您的测试数据完成的方法:
create table t1 (loan_number int, pid int, total int, newPid int, flag int );
insert into t1 (loan_number, pid, total ) values (411001,1,100), (411001,2,-100), (411001,3,50), (411001,4,-50), (411001,5,50), (411001,6,25);
update pos set newPid=neg.pid, flag=1 from
( select loan_number, pid, total,
row_number() over (partition by total order by pid) rn
from t1
where total>0) pos
inner join
(select loan_number, pid, total,
row_number() over (partition by total order by pid) rn
from t1
where total<0) neg
on neg.loan_number = pos.loan_number
and neg.total =- pos.total
and neg.rn = pos.rn
;