2009-07-31 5 views
1

먼저 테이블과 배경을 설정하는 스크립트가 있습니다.오라클 잠금 및 summarisation에 대한 질문

CREATE TABLE TEST_P 
(
    ID NUMBER(3) NOT NULL PRIMARY KEY, 
    SRC VARCHAR2(2) NOT NULL, 
    DEST VARCHAR2(2) NOT NULL, 
    AMT NUMBER(4) NOT NULL, 
    B_ID_SRC NUMBER(3), 
    B_ID_DEST NUMBER(3) 
); 

이 테이블의 행은 AMT는 SRCDEST에서 이동되는 것을 나타낸다. ID 열은 서로 게이트 키입니다. 첫 번째 행은 10 개의 물건이 B1에서 S1으로 이동 중임을 나타냅니다. SRCDEST의 값이 다릅니다. 두 값 모두 같은 값을 사용할 수 없습니다.

INSERT INTO TEST_P VALUES (1, 'B1', 'S1', 10, NULL, NULL); 
INSERT INTO TEST_P VALUES (2, 'B2', 'S1', 20, NULL, NULL); 
INSERT INTO TEST_P VALUES (3, 'B3', 'S2', 40, NULL, NULL); 
INSERT INTO TEST_P VALUES (4, 'B1', 'S2', 80, NULL, NULL); 
INSERT INTO TEST_P VALUES (5, 'B4', 'S2', 160,NULL, NULL); 

이와 비슷한 표가 있습니다. 동일한 정보에 대해 다른 시각을 가지고 있습니다. 여기의 각 행은 "누가"에서 추가되거나 제거 된 것을 나타냅니다. 의 값은 B1, B2 ..하고 S1, S2 ... 주기적 TEST_P의 값을 가지고 TEST_B을 채우는 방법을 쓸 필요

CREATE TABLE TEST_B 
(
    ID  NUMBER(3) NOT NULL PRIMARY KEY, 
    BATCH NUMBER(3) NOT NULL, 
    WHO  VARCHAR2(2) NOT NULL, 
    AMT  NUMBER(4) NOT NULL 
); 

CREATE SEQUENCE TEST_B_SEQ START WITH 100; 

이다. 외래 키인 B_ID_SRCB_ID_DESTTEST_B에 업데이트해야합니다.

여기까지 내 솔루션입니다.

단계 1 :

INSERT INTO TEST_B 
(ID, BATCH, WHO, AMT) 
SELECT TEST_B_SEQ.NEXTVAL, 42, WHO, AMT FROM 
(
    SELECT SRC AS WHO, SUM(AMT) AMT FROM TEST_P 
    WHERE B_ID_SRC IS NULL AND B_ID_DEST IS NULL 
    GROUP BY SRC 
    UNION ALL 
    SELECT DEST, -SUM(AMT) FROM TEST_P 
    WHERE B_ID_SRC IS NULL AND B_ID_DEST IS NULL 
    GROUP BY DEST) 
; 

단계 2 :

이 두 가지 문제가있다
UPDATE TEST_P 
    SET B_ID_SRC = (SELECT ID FROM TEST_B WHERE BATCH = 42 AND TEST_P.SRC = WHO), 
     B_ID_DEST = (SELECT ID FROM TEST_B WHERE BATCH = 42 AND TEST_P.DEST = WHO); 

:

1) SELECT의 행이 고정되어야이. FOR UPDATE으로 어떻게 선택합니까?

2) 행이 다른 세션에 의해 삽입되고 1.5 단계에서 커밋 된 경우 UPDATE는 INSERT보다 많은 행을 잡아냅니다. 행 단위 처리로 되돌리지 않고 어떻게 해결해야합니까?

세부 정보 실제 TEST_P 테이블에 상태 열이 있습니다. 상황이 올바른 상태 일 때만 TEST_B에 포함됩니다.

다양한 이유로 인해 TEST_B이 실제로 필요합니다. 나는 그것을 단지 하나의 견해로 만들 수 없다. 후속 처리 등이 있습니다. 예를 들어 TEST_P의 모든 행을 업데이트하려는 경우

답변

3

을 입력하십시오. 두 가지 간단한 솔루션을 사용하면 두 테이블의 정보가 일치하는지 확인할 수 있습니다. 당신은 할 수 중 하나 : 당신의 거래 기간 (다른 삽입 세션을 기다려야 할 것이다) 또는

  • ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE이 시작 후 다른 세션에 의해 변경 사항을 확인하기 위해 첫 번째 세션을 방지 할에 대한

    1. LOCK TABLE test_p IN EXCLUSIVE MODE 거래.여기

      session 1> ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE; 
      
      Session altered 
      
      session 1> INSERT INTO TEST_B 
           2 (ID, BATCH, WHO, AMT) 
           3 SELECT TEST_B_SEQ.NEXTVAL, 42, WHO, AMT FROM 
           4 (
           5 SELECT SRC AS WHO, SUM(AMT) AMT FROM TEST_P 
           6 WHERE B_ID_SRC IS NULL AND B_ID_DEST IS NULL 
           7 GROUP BY SRC 
           8 UNION ALL 
           9 SELECT DEST, -SUM(AMT) FROM TEST_P 
           10 WHERE B_ID_SRC IS NULL AND B_ID_DEST IS NULL 
           11 GROUP BY DEST) 
           12 ; 
      
      6 rows inserted 
      

      내가 다른 세션으로 행을 삽입하고 커밋 :

      session 2> INSERT INTO TEST_P VALUES (6, 'B4', 'S2', 2000,NULL, NULL); 
      
      1 row inserted 
      
      session 2> commit; 
      
      Commit complete 
      

      세션 1은 행 삽입 보지 않는다

    방법 1 I는 방법 2를 설명 할 것이다, 간단 아직 세션 2 :

    session 1> select * from TEST_P; 
    
        ID SRC DEST AMT B_ID_SRC B_ID_DEST 
    ---- --- ---- ----- -------- --------- 
        1 B1 S1  10   
        2 B2 S1  20   
        3 B3 S2  40   
        4 B1 S2  80   
        5 B4 S2  16 
    
    session 1> UPDATE TEST_P 
         2 SET B_ID_SRC = (SELECT ID FROM TEST_B WHERE BATCH = 42 AND TEST_P.SRC = WHO), 
         3  B_ID_DEST = (SELECT ID FROM TEST_B WHERE BATCH = 42 AND TEST_P.DEST = WHO); 
    
    5 rows updated 
    
    session 1> commit; 
    
    Commit complete 
    

    결과는 다음과 같습니다. 일관성, 세션 1 커밋 세션 2에 의해 삽입 된 행이 표시됩니다 후 : 하나의 MERGE 문은 여기 요구 사항을 처리 할 수 ​​

    session 1> select * from TEST_P; 
    
        ID SRC DEST AMT B_ID_SRC B_ID_DEST 
    ---- --- ---- ----- -------- --------- 
        6 B4 S2 2000   
        1 B1 S1  10  100  104 
        2 B2 S1  20  101  104 
        3 B3 S2  40  102  105 
        4 B1 S2  80  100  105 
        5 B4 S2  160  103  105 
    
    6 rows selected 
    
  • +0

    감사합니다. 빈센트, 이것이 내가 필요한 것입니다. 실은 다른 일이 없기 때문에 전체 테이블을 잠글 수는 없습니다. 직렬화가 가능한 트랜잭션이 필요합니다. –

    1

    합니다.

    http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_9016.htm#SQLRF01606

    귀하의 성명는 다음과 같다 :

    MERGE INTO TEST_B 
    USING 
    (
        SELECT SRC AS WHO, SUM(AMT) AMT FROM TEST_P 
        WHERE B_ID_SRC IS NULL AND B_ID_DEST IS NULL 
        GROUP BY SRC 
        UNION ALL 
        SELECT DEST, -SUM(AMT) FROM TEST_P 
        WHERE B_ID_SRC IS NULL AND B_ID_DEST IS NULL 
        GROUP BY DEST) 
    ON (
    WHEN MATCHED THEN UPDATE SET ...; 
    

    이 사용하여 목표 테이블에 가입 불구하고 업데이트해야 행을 식별하는 데 더 effiecient 수 있습니다 절을 사용하여 수정할 필요가없는 행을 업데이트하지 않도록합니다.

    +0

    @David : 하나의 MERGE 문은 두 테이블을 수정할 수 없습니다 (예 : TEST_B의 행 삽입 및 TEST_P 업데이트) –

    +0

    오, 죄송합니다. –