programing

Oracle: UPSERT(업데이트 또는 테이블에 삽입) 방법

javamemo 2023. 3. 6. 20:37
반응형

Oracle: UPSERT(업데이트 또는 테이블에 삽입) 방법

UPSERT 작업은 테이블에 데이터와 일치하는 행이 이미 있는지 여부에 따라 테이블에 행을 업데이트하거나 삽입합니다.

if table t has a row exists that has key X:
    update t set mystuff... where mykey=X
else
    insert into t mystuff...

Oracle에는 특정 UPSERT 문구가 없기 때문에 이를 위한 가장 좋은 방법은 무엇입니까?

MERGE 문은 두 테이블 간의 데이터를 병합합니다.DUAL을 사용하면 이 명령을 사용할 수 있습니다.이는 동시 액세스로부터 보호되지 않습니다.

create or replace
procedure ups(xa number)
as
begin
    merge into mergetest m using dual on (a = xa)
         when not matched then insert (a,b) values (xa,1)
             when matched then update set b = b+1;
end ups;
/
drop table mergetest;
create table mergetest(a number, b number);
call ups(10);
call ups(10);
call ups(20);
select * from mergetest;

A                      B
---------------------- ----------------------
10                     2
20                     1

위의 PL/SQL에 있는 듀얼 예시는 매우 좋았습니다.왜냐하면 저는 비슷한 일을 하고 싶었지만 클라이언트 측에서 하고 싶었기 때문입니다.여기 C#에서 직접 유사한 스테이트먼트를 전송하기 위해 사용한SQL이 있습니다.

MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name") 
    VALUES ( 2097153,"smith", "john" )

단, C#의 관점에서 보면 업데이트 실행 및 영향을 받는 행이 0인지 확인하고 해당 행이 0인지 삽입되었는지 여부를 확인하는 것보다 속도가 느려집니다.

MERGE의 대안('구식 방식'):

begin
   insert into t (mykey, mystuff) 
      values ('X', 123);
exception
   when dup_val_on_index then
      update t 
      set    mystuff = 123 
      where  mykey = 'X';
end;   

예외 검사를 사용하지 않는 다른 대안:

UPDATE tablename
    SET val1 = in_val1,
        val2 = in_val2
    WHERE val3 = in_val3;

IF ( sql%rowcount = 0 )
    THEN
    INSERT INTO tablename
        VALUES (in_val1, in_val2, in_val3);
END IF;
  1. 없는 경우 삽입
  2. 업데이트:
    
마이테이블에 삽입(id1, t1)듀얼에서 11, 'x1' 선택존재하지 않는 곳(mytble에서 id1 WHERE 1 = 11);
업데이트 mytable SET t1 = 'x1' WHERE ID1 = 11;

팀 실베스터의 코멘트에서 지적된 바와 같이 동시 접속이 가능한 상황에서 지금까지 나온 답변 중 어느 것도 안전하지 않으며, 레이스의 경우 예외를 제기할 것이다.이 문제를 해결하려면 insert/update 콤보를 일종의 루프 스테이트먼트로 감싸야 합니다.그러면 예외 발생 시 모든 것을 재시도할 수 있습니다.

예를 들어, Grommit의 코드를 루프로 감싸 동시에 실행할 때 안전하게 할 수 있는 방법은 다음과 같습니다.

PROCEDURE MyProc (
 ...
) IS
BEGIN
 LOOP
  BEGIN
    MERGE INTO Employee USING dual ON ( "id"=2097153 )
      WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
      WHEN NOT MATCHED THEN INSERT ("id","last","name") 
        VALUES ( 2097153,"smith", "john" );
    EXIT; -- success? -> exit loop
  EXCEPTION
    WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted
      NULL; -- exception? -> no op, i.e. continue looping
    WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted
      NULL; -- exception? -> no op, i.e. continue looping
  END;
 END LOOP;
END; 

트랜잭션 모드인 N.B.SERIALIZABLEORA-08177: 트랜잭션 예외에 대한 액세스를 직렬화할 수 없습니다.

그로밋이 대답하고 싶은데, 다만 중복된 값이 필요해요.한 번 표시되는 솔루션을 찾았습니다.http://forums.devshed.com/showpost.php?p=1182653&postcount=2

MERGE INTO KBS.NUFUS_MUHTARLIK B
USING (
    SELECT '028-01' CILT, '25' SAYFA, '6' KUTUK, '46603404838' MERNIS_NO
    FROM DUAL
) E
ON (B.MERNIS_NO = E.MERNIS_NO)
WHEN MATCHED THEN
    UPDATE SET B.CILT = E.CILT, B.SAYFA = E.SAYFA, B.KUTUK = E.KUTUK
WHEN NOT MATCHED THEN
    INSERT (  CILT,   SAYFA,   KUTUK,   MERNIS_NO)
    VALUES (E.CILT, E.SAYFA, E.KUTUK, E.MERNIS_NO); 

난 몇 년 동안 첫 번째 코드 샘플을 사용해왔어.카운트가 아니라 not found(찾을 수 없음)입니다.

UPDATE tablename SET val1 = in_val1, val2 = in_val2
    WHERE val3 = in_val3;
IF ( sql%notfound ) THEN
    INSERT INTO tablename
        VALUES (in_val1, in_val2, in_val3);
END IF;

아래 코드는 새로운 개선 코드일 수 있습니다.

MERGE INTO tablename USING dual ON ( val3 = in_val3 )
WHEN MATCHED THEN UPDATE SET val1 = in_val1, val2 = in_val2
WHEN NOT MATCHED THEN INSERT 
    VALUES (in_val1, in_val2, in_val3)

첫 번째 예에서는 업데이트가 인덱스를 검색합니다.오른쪽 행을 업데이트하려면 이 작업을 수행해야 합니다.Oracle은 암묵적인 커서를 열고 해당 삽입을 래핑하기 위해 이 커서를 사용합니다.그러면 삽입은 키가 존재하지 않는 경우에만 이루어집니다.그러나 insert는 독립된 명령어이므로 두 번째 검색을 수행해야 합니다.merge 명령어의 내부 동작은 알 수 없지만 명령어가 단일 단위이기 때문에 Oracle은 단일 인덱스 룩업으로 올바른 삽입 또는 업데이트를 실행할 수 있습니다.

일부 테이블에서 데이터를 가져와 테이블을 업데이트하거나 행을 삽입하거나 삭제하는 등의 처리가 필요한 경우에는 병합이 더 낫다고 생각합니다.단, 단일 행의 경우 구문이 더 일반적이기 때문에 첫 번째 경우를 고려할 수 있습니다.

다음 두 가지 솔루션에 대한 참고 사항:

1) 예외인 경우 삽입 후 갱신한다.

또는

2) sql%rowcount = 0인 경우 업데이트한 후

먼저 삽입할지 업데이트할지 여부도 애플리케이션에 따라 달라집니다.추가 삽입 또는 추가 업데이트가 필요합니까?성공할 가능성이 가장 높은 사람이 먼저 가야 한다.

잘못된 항목을 선택하면 불필요한 색인 읽기가 많이 발생합니다.큰 문제는 아니지만 고려해야 할 사항입니다.

이거 드셔보세요.

insert into b_building_property (
  select
    'AREA_IN_COMMON_USE_DOUBLE','Area in Common Use','DOUBLE', null, 9000, 9
  from dual
)
minus
(
  select * from b_building_property where id = 9
)
;

http://www.praetoriate.com/oracle_tips_upserts.htm 에서 :

"Oracle9i에서는 UPSERT가 하나의 스테이트먼트로 이 작업을 수행할 수 있습니다."

INSERT
FIRST WHEN
   credit_limit >=100000
THEN INTO
   rich_customers
VALUES(cust_id,cust_credit_limit)
   INTO customers
ELSE
   INTO customers SELECT * FROM new_customers;

언급URL : https://stackoverflow.com/questions/237327/oracle-how-to-upsert-update-or-insert-into-a-table

반응형