我在Oracle RDBMS中工作。
假设我在存储过程的上下文中有以下UPDATE语句:
UPDATE memberPlan mp /* C$MP$MEMBERPLANID */
SET ( mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber ) = (
SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */
htu.contractId as lastContractId ,
CASE WHEN htu.contractId IS NULL THEN
NULL
ELSE
varRunDate
END as lastContractIdChanged ,
htu.groupOrPolicyNumber as lastGroupOrPolicyNumber
FROM htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
WHERE htu.memberPlanId = mp.memberPlanId)
WHERE EXISTS (
SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */ 1
FROM htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
WHERE htu.memberPlanId = mp.memberPlanId);
问题是memberplan表被锁定了 程序正在运行。
我想一次只排行锁定,更新和释放一行 并将表中的其余行保持解锁状态。
所以我设计了以下解决方案:
DECLARE
CURSOR memberPlan1_cur(parmRunDate IN DATE) IS
SELECT
mp.lastContractId as lastContractId ,
mp.lastContractIdChanged as lastContractIdChanged ,
mp.lastGroupOrPolicyNumber as lastGroupOrPolicyNumber ,
htu.lastContractId as updLastContractId ,
htu.lastContractIdChanged as updLastContractIdChanged ,
htu.lastGroupOrPolicyNumber as updLastGroupOrPolicyNumber
FROM
memberPlan mp
INNER JOIN
(SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */
memberPlanId as memberPlanId ,
contractId as lastContractId ,
CASE WHEN contractId IS NULL THEN
NULL
ELSE
parmRunDate
END as lastContractIdChanged ,
groupOrPolicyNumber as lastGroupOrPolicyNumber
FROM htUpdateMemberPlan) htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
ON mp.memberPlanId = htu.memberPlanId
WHERE EXISTS (
SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */ 1
FROM htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
WHERE htu.memberPlanId = mp.memberPlanId)
FOR UPDATE OF
mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber ;
BEGIN
FOR memberPlan1_row IN memberPlan1_cur(varRunDate) LOOP
UPDATE memberPlan mp /* C$MP$MEMBERPLANID */
SET mp.lastContractId = memberPlan1_row.updLastContractId ,
mp.lastContractIdChanged = memberPlan1_row.updLastContractIdChanged ,
mp.lastGroupOrPolicyNumber = memberPlan1_row.updLastGroupOrPolicyNumber
WHERE CURRENT OF memberPlan1_cur;
END LOOP;
COMMIT;
END;
我基本上都会返回更新值 我要更新的源字段。
上述解决方案可针对以下代码进行优化,
上面SQL的一个变体,其中存在测试是
取而代之的是直接INNER JOIN:
DECLARE
CURSOR memberPlan1_cur(parmRunDate IN DATE) IS
SELECT /*+ FULL(mp) INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */
mp.lastContractId as lastContractId ,
mp.lastContractIdChanged as lastContractIdChanged ,
mp.lastGroupOrPolicyNumber as lastGroupOrPolicyNumber ,
htu.contractId as updLastContractId ,
CASE WHEN htu.contractId IS NULL THEN
NULL
ELSE
parmRunDate
END as updLastContractIdChanged ,
htu.groupOrPolicyNumber as updLastGroupOrPolicyNumber
FROM
memberPlan mp
INNER JOIN htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
ON mp.memberPlanId = htu.memberPlanId
FOR UPDATE OF
mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber ;
BEGIN
FOR memberPlan1_row IN memberPlan1_cur(varRunDate) LOOP
UPDATE memberPlan mp /* C$MP$MEMBERPLANID */
SET mp.lastContractId = memberPlan1_row.updLastContractId ,
mp.lastContractIdChanged = memberPlan1_row.updLastContractIdChanged ,
mp.lastGroupOrPolicyNumber = memberPlan1_row.updLastGroupOrPolicyNumber
WHERE CURRENT OF memberPlan1_cur;
END LOOP;
COMMIT;
END;
我考虑过以下另一种方式:
DECLARE
CURSOR memberPlan1_cur IS
SELECT
mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber
FROM memberPlan mp
WHERE EXISTS (
SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */ 1
FROM htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
WHERE htu.memberPlanId = mp.memberPlanId)
FOR UPDATE OF
mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber ;
BEGIN
FOR memberPlan1_row IN memberPlan1_cur LOOP
UPDATE memberPlan mp
SET (mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber ) =
(SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */
htu.contractId as lastContractId ,
CASE WHEN htu.contractId IS NULL THEN
NULL
ELSE
varRunDate
END as lastContractIdChanged ,
htu.groupOrPolicyNumber as lastGroupOrPolicyNumber
FROM htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
WHERE mp.memberPlanId = htu.memberPlanId)
WHERE CURRENT OF memberPlan1_cur;
END LOOP;
COMMIT;
END;
上面的解决方案我相信会给我带来问题因为 更新不是单独分配值,如
SET column1=value1,column2=value2,column3=value3
但使用
SET (column1,column2,column3) = (SELECT ...) syntax
我想到了使用第二种方法实现目标的第三种方式 光标和3个变量:
DECLARE
varLastContractId memberPlan.lastContractId%TYPE ;
varLastContractIdChanged memberPlan.lastContractIdChanged%TYPE ;
varLastGroupOrPolicyNumber memberPlan.lastGroupOrPolicyNumber%TYPE ;
CURSOR memberPlan1_cur IS
SELECT
mp.memberPlanId ,
mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber
FROM memberPlan mp
WHERE EXISTS (
SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */ 1
FROM htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
WHERE htu.memberPlanId = mp.memberPlanId)
FOR UPDATE OF
mp.lastContractId ,
mp.lastContractIdChanged ,
mp.lastGroupOrPolicyNumber ;
CURSOR htUpdateMemberPlan1_cur(
parmRunDate IN DATE ,
parmMemberPlanId IN NUMBER) IS
SELECT /*+ INDEX(htu,HTUPDATEMEMBERPLAN$MEMBERPLAN) */
htu.contractId as lastContractId ,
CASE WHEN htu.contractId IS NULL THEN
NULL
ELSE
parmRunDate
END as lastContractIdChanged ,
htu.groupOrPolicyNumber as lastGroupOrPolicyNumber
FROM htUpdateMemberPlan htu /* HTUPDATEMEMBERPLAN$MEMBERPLAN */
WHERE htu.memberPlanId = parmMemberPlanId;
BEGIN
FOR memberPlan1_row IN memberPlan1_cur LOOP
OPEN htUpdateMemberPlan1_cur(
varRunDate ,
memberPlan1_row.memberPlanId );
FETCH htUpdateMemberPlan1_cur INTO
varLastContractId ,
varLastContractIdChanged ,
varLastGroupOrPolicyNumber ;
UPDATE memberPlan mp
SET mp.lastContractId = varLastContractId ,
mp.lastContractIdChanged = varLastContractIdChanged ,
mp.lastGroupOrPolicyNumber = varLastGroupOrPolicyNumber
WHERE CURRENT OF memberPlan1_cur;
CLOSE htUpdateMemberPlan1_cur;
END LOOP;
COMMIT;
END;
上述三种CURSOR解决方案中的哪一种是正确的解决方法?
答案 0 :(得分:0)
关闭袖口我会猜测第一个游标版本的性能会比第二个游标版本好(假设它们的结果相同)。推理:使用htUpdateMemberPlan执行单个内部联接(第一个游标版本)将比第二个版本的多个联接(游标循环中的每次迭代一次)执行得更好。但这真是一个猜测。我会运行一个sql跟踪并查看发生了什么。
似乎第一个版本从htUpdateMemberPlan中检索值的静态快照,该快照一直在游标循环中使用。第二个版本在处理每一行时对htUpdateMemberPlan进行查找。因此,有可能的是,varRunDate的值可能会在创建游标的时间与第二个版本中处理游标的特定行之间的另一个会话中更改。但不是在第一个版本中。这对两种方法的意义略有不同。你想要哪个?再次,只是挥手在这里。可能是错的。
还想知道CASE语句是否可能被NVL2(contractId,varRunDate,null)替换。然后,也许再好不过了:)