ORA-38104: ON 절에서 참조되는 열은 업데이트할 수 없습니다.
삭제가 끝난 심플한 테이블이 있습니다(삭제하지 않고 이 컬럼에서 갱신할 필요가 있습니다).
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
IDELECT = 0인 일치 레코드가 있는 경우 기본 키 위반을 예외로 지정하므로 "TARGET"을 이동할 수 없습니다.ISDELETED = 업데이트 스테이트먼트에서 업데이트 스테이트먼트로의 1인치.
받아들여진 응답과는 달리 실제로 이 작업을 수행하는 방법이 있습니다.즉, 문제의 비트를 ON 절에서 WHERE 절로 이동하여 업데이트 스테이트먼트의 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);
컬럼을 어떤 식으로 표현하고 이름을 바꾸면 효과가 있을 것 같습니다.예제에서는 음음 in in 。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);
주의:
- 이름만 바꾸면 안 돼요.파서는 아직 같은 컬럼임을 검출할 수 있을 정도로 「스마트」한 것 같습니다.그러나 이름을 바꾸고 "silly" 표현으로 표현하면 파서를 능가합니다.
- 이것은 분명히 대가가 따른다.이름이 바뀐 열에서 인덱스를 쉽게 사용할 수 없을 수 있으므로 실행 계획을 확인하십시오.이 예에서는 동작할 가능성이 있습니다.
- Oracle은 향후 이 문제를 "수정"하여 ORA-38104 탐지의 일관성을 높일 수 있으므로 이 해결 방법이 실패할 수 있습니다.
이것도 효과가 있는 것 같습니다만, 합리적인 인덱스 사용은 전혀 허가되지 않는 것 같습니다(사용하시는 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);
저는 이 회피책(및 실행 계획)에 대해 블로그에 올렸습니다.
이 경우엔 사격 후 룩 알고리즘이 더 나을 것 같네요
자주 발생할 것으로 예상되는 사례에 따라 다음 중 하나를 수행합니다.
- 업데이트 후 행이 업데이트되지 않으면 삽입 또는
- 삽입하고 키 위반이 있는 경우 업데이트하십시오.
아래 시나리오도 고려해야 합니다.
하는 IDELETED = 0
프라이머리 키 위반을 예외로 하고 싶기 때문에 TARGET을 이동할 수 없습니다.ISDELETED = 업데이트 스테이트먼트에서 업데이트 스테이트먼트로의 1인치.
그래서 정확한 해결책은 다음과 같습니다.
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;
이거면 되지 않을까요?
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);
「이러한 스테이트먼트에 WHERE 절을 사용하면, 조건부 삽입과 갱신이 가능하게 되었습니다.
언급URL : https://stackoverflow.com/questions/5900912/ora-38104-columns-referenced-in-the-on-clause-cannot-be-updated
'programing' 카테고리의 다른 글
스프링 부트 단일 페이지 응용 프로그램 - 모든 요청을 index.html로 전송합니다. (0) | 2023.03.18 |
---|---|
React.js - 재렌더 시 입력이 초점을 잃음 (0) | 2023.03.18 |
woocommerce 함수로 작성된 함수를 덮어쓰려고 합니다.php 파일 (0) | 2023.03.18 |
단일 스프링 부트 테스트 속성 재정의 (0) | 2023.03.18 |
$exists가 $cond 내에 있는 조건부 그룹화 (0) | 2023.03.18 |