2009-07-31 5 views
1

まず、テーブルと背景を設定するためのスクリプトをいくつか示します。Oracleのロックと集約についての質問

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がSRCからDESTに移動されていることを示します。 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); 

このような別のテーブルがあります。それは同じ情報の異なる見解を持っています。ここの各行は、「Who」に追加または削除されたものを示しています。値は、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:

1)SELECTの行がロックされるべきである:

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); 

これには二つの問題があります。 FOR UPDATEでこの選択を行うにはどうすればよいですか?

2)行が別のセッションによって挿入され、ステップ1.5でコミットされた場合、UPDATEはINSERTより多くの行を取得します。行ごとの処理に戻さずにこれを解決するにはどうすればよいですか?

詳細 実際のTEST_Pテーブルにはステータスカラムがあります。物事が正しい状態にあるときだけ、それはTEST_Bに含まれます。

実際にはTEST_Bが必要です。私はそれをただのものにすることはできません。その後の処理などがあります。TEST_Pのすべての行を更新する場合は、

答えて

3

があります。 2つの簡単なソリューションを使用すると、両方のテーブルの情報が一貫していることを確認できます。あなたは可能性のいずれか:あなたのトランザクションの期間(他の挿入セッションが待たなければならない)、または

  • 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私は方法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によって挿入された行が表示されます。

    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

    ここでは1つのMERGE文で要件を処理できます。

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

    あなたの文は、の線に沿って何かのようになります。使用してターゲット表に参加しても更新する必要がある行を識別するために、よりeffiecientかもしれない

    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 ...; 
    

    変更する必要のない行を更新しないようにします。

    +0

    @David:1つのMERGEステートメントは、2つのテーブルを変更することができません(つまり、TEST_Bの行を挿入してTEST_Pを更新します)。 –

    +0

    ああ、申し訳ありません。 –

    関連する問題