削除フラグ付きのシンプルなテーブルがあります(レコードは、削除するのではなく、この列で更新する必要があります)。
create table PSEUDODELETETABLE
(
ID NUMBER(8) not null, -- PKEY
NAME VARCHAR2(50) not null,
ISDELETED NUMBER(1) default 0 not null
)
新しいレコードを挿入するときに、主キーに一致するレコードがすでに存在するかどうかを確認する必要がありますが、ISDELETED = 1です。その場合、ISDELETEDを0に変更して、他の列を更新する必要があります。したがって、次のMerge-Statementを使用しています。
merge into ET.PSEUDODELETETABLE TARGET
using (select 1 as ID, 'Horst' as NAME from sys.dual) SOURCE
on (TARGET.ISDELETED = 1 and SOURCE.ID = TARGET.ID)
when matched then
update set ISDELETED = 0, NAME = SOURCE.NAME
when not matched then
insert values (SOURCE.ID, SOURCE.NAME, 0);
Sql-Serverではうまく機能しますが、Oracleは次のように述べています。
ORA-38104: Columns referenced in the ON Clause cannot be updated: TARGET.ISDELETED
IDELETED = 0の一致するレコードがある場合、例外として主キー違反が必要です。そのため、 "TARGET.ISDELETED = 1"をon-clauseからupdate-statementに移動できません。
この場合は、撮影後のルックアップアルゴリズムを使用する方がよいでしょう。
あなたがより頻繁なケースであると予想するものに応じて、次のいずれか:
受け入れられた応答とは対照的に、実際にはこれを回避する方法があります。問題のビットをON句から外して、更新ステートメントのWHERE句に移動します。
merge into ET.PSEUDODELETETABLE TARGET
using (select 1 as ID, 'Horst' as NAME from sys.dual) SOURCE
on (SOURCE.ID = TARGET.ID)
when matched then
update
set ISDELETED = 0,
NAME = SOURCE.NAME
where TARGET.ISDELETED = 1 -- Magic!
when not matched then
insert
values (SOURCE.ID, SOURCE.NAME, 0);
列を何らかの式に入れて名前を変更すると、うまくいくようです。以下の例では、ISDELETED_
とISDELETED
は事実上同じものです:
merge into (
select nvl(ISDELETED, ISDELETED) as ISDELETED_, ISDELETED, ID,
from ET.PSEUDODELETETABLE
) TARGET
using (select 1 as ID, 'Horst' as NAME from sys.dual) SOURCE
on (TARGET.ISDELETED_ = 1 and SOURCE.ID = TARGET.ID) -- Use the renamed version here
when matched then
update set ISDELETED = 0, NAME = SOURCE.NAME -- Use the original version here
when not matched then
insert values (SOURCE.ID, SOURCE.NAME, 0);
通知:
これも機能しているようですが、適切なインデックスの使用は許可されていないようです(ご使用のOracleのバージョンでもう一度確認してください)。
merge into ET.PSEUDODELETETABLE TARGET
using (select 1 as ID, 'Horst' as NAME from sys.dual) SOURCE
on ((select TARGET.ISDELETED from dual) = 1 and SOURCE.ID = TARGET.ID)
when matched then
update set ISDELETED = 0, NAME = SOURCE.NAME
when not matched then
insert values (SOURCE.ID, SOURCE.NAME, 0);
これでも機能します(これにより、ORA-38104チェック全体について深刻な疑問が生じます)。
merge into ET.PSEUDODELETETABLE TARGET
using (select 1 as ID, 'Horst' as NAME from sys.dual) SOURCE
on ((TARGET.ISDELETED, 'dummy') = ((1, 'dummy')) and SOURCE.ID = TARGET.ID)
when matched then
update set ISDELETED = 0, NAME = SOURCE.NAME
when not matched then
insert values (SOURCE.ID, SOURCE.NAME, 0);
これはうまくいきませんか?
merge into (select * from ET.PSEUDODELETETABLE where ISDELETED = 1) TARGET
using (select 1 as ID, 'Horst' as NAME from sys.dual) SOURCE
on (SOURCE.ID = TARGET.ID)
when matched then
update set ISDELETED = 0, NAME = SOURCE.NAME
when not matched then
insert values (SOURCE.ID, SOURCE.NAME, 0);
以下のシナリオも考慮する必要があります。
IDELETED = 0
と一致するレコードがある場合、例外として主キー違反が必要です。そのため、「TARGET.ISDELETED = 1」をon-clauseからupdate-statementに移動できません。
正確な解決策は次のとおりです、
begin
update ET.PSEUDODELETETABLE set ISDELETED = 0, NAME = 'Horst'
where ISDELETED = 1 and ID = 1;
if (sql%rowcount = 0) then
insert into ET.PSEUDODELETETABLE values (1, 'Horst', 0);
end if;
end;