Я работаю в 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);
Проблема в том, что таблица члена плана блокируется
во время выполнения процедуры.
Я бы хотел блокировать, обновлять и освобождать только одну строку за раз
и оставьте остальные строки в таблице незапертыми.
Итак, я разработал следующее решение:
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 выше, где проверка существования
заменено на прямое внутреннее соединение:
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 является правильным способом для этого?