- PR -

同期処理時を行うPLSQLでエラーが出てしまう

1
投稿者投稿内容
始まり
常連さん
会議室デビュー日: 2006/10/03
投稿数: 31
投稿日時: 2009-03-25 18:23
Oracle9iで作成したデータベースの同期処理を行うPLSQLの作成を行っているのですが、今回PLSQLを触るのは初めてでSQLも苦手なものでアドバイスでも頂ければと思い書き込みました。

教えて頂きたい内容は、TEST1のテーブルからIDとKBNにひもづく更新日時が最新のデータを取得する方法です。

何度か書き換えてやってみたのですが「フェッチされています。」と表示されてしまいうまく行きませんでした。

同期の内容を下記に記述いたしました。
説明不足ではあると思いますがよろしくお願いいたします。


同期の内容はTEST1とTEST2データを比較して、下記の動作を行います。
・IDとKBN同一のデータが無ければINSERT
・IDとKBN同一のデータが有ればDATEを見て同期元の更新日時が新しければUPDATE


以下は作成したPLSQLとトリガーより自動生成された同期管理テーブルのデータです。

TABLENAME:TEST1
ID KBN DATE
_______________________
000000000001 A 09-03-23
000000000001 A 09-03-25
000000000002 A 09-03-23
000000000003 A 09-03-23
000000000003 A 09-03-25
000000000004 A 09-03-25
000000000006 B 09-03-25


TABLENAME:TEST2
ID KBN DATE
_______________________
000000000001 A 09-03-23
000000000001 A 09-03-25
000000000002 A 09-03-23
000000000003 A 09-03-23
000000000003 A 09-03-25
000000000004 A 09-03-25
000000000004 B 09-03-25

__________________

DECLARE
 ct VARCHAR2(256);
 dt VARCHAR2(256);
 yt VARCHAR2(256);
/* カーソル */
 CURSOR SYNCHRO IS
 SELECT DISTINCT ID, KBN
 FROM TEST1
 WHERE UP_DT IN (SELECT MAX(UP_DT) FROM TEST1);
 TEMP SYNCHRO%ROWTYPE;
BEGIN
 OPEN SYNCHRO;
  LOOP
   FETCH SYNCHRO INTO TEMP;
   EXIT WHEN SYNCHRO%NOTFOUND;
   SELECT DISTINCT ID,KBN,UP_DT INTO CT,YT,DT
   FROM TEST2@TEST
   WHERE UP_DT IN (SELECT MAX(UP_DT) FROM TEST2);
   IF TEMP.ID = '' THEN
    ......
   ELSE
    ......
   END IF;
  END LOOP;
 CLOSE SYNCHRO;
EXCEPTION
..........
END;
/
よっしー
大ベテラン
会議室デビュー日: 2007/05/17
投稿数: 143
投稿日時: 2009-03-26 00:22
始まりさん、こんにちは。

引用:
同期の内容はTEST1とTEST2データを比較して、下記の動作を行います。
・IDとKBN同一のデータが無ければINSERT
・IDとKBN同一のデータが有ればDATEを見て同期元の更新日時が新しければUPDATE


ループするまでもなく、UPDATE1回とINSERT1回を実行するのはどうでしょう?
始まり
常連さん
会議室デビュー日: 2006/10/03
投稿数: 31
投稿日時: 2009-03-30 11:19
よっしー様回答ありがとうございます。

体調を崩しており返事が送れたことをお詫びいたします。

UPDATE、INSERT1回で出来れば楽なのですが、仕様だと

INSERT、DELETEのみで処理を行う。UPDATEは使用不可、
同期処理のデータ1件毎にコミットを行いエラーが発生した場合ロールバックを行うとなっています。

後一番大事な事を書き忘れていましたがTEST1とTEST2のデータは比較するだけであって
実際に同期を行うテーブルは別になります。

TEST1、TEST2のテーブルにプライマリキーは設定されていませんが、同期を行うテーブルにはID,KBNがプライマリキーとして設定されています。

DBとテーブルは以下の用になっています。
-------------------------------
同期1
TEST1
----------上下別サーバー-------
同期2
TEST2
-------------------------------

同期1テーブルにデータが挿入されると、挿入されたデータのID,KBN,DATE(SYSDATE)がTEST1に挿入される、同データを更新した回数だけ同ID,同KBN,DATEが挿入される
(同期2も同様)


同期の流れは以下のように1件ずつ判定を行う為、全件の処理を一括で行う事は出来ないです。

TEST1のデータ
「例)
000000001 A 2009/03/31
000000001 A 2009/03/28
000000001 A 2009/03/12」
この用にキー項目が同一のデータが複数あった場合、最新の日時の物を取得する。
取得したキー項目と同一のデータをTEST2から取得して同期判定を行う。
TEST1のDATEがTEST2より新しければTEST1のキーと同一のデータを同期2からDELETEして、TEST1のキーと同一のデータを同期1から取得して、同期2にINSERTする。
その後、更新を行ったID,KBNにひもづく全てのデータをTEST1,TEST2からDELEEする。
このとき同期処理が正常に終わっていたらコミット、エラーが発生したらロールバックを行う。




投稿してからの進捗はTEST1、TEST2の比較を行う事が出来ましたが、
「TEST1のデータがTEST2に無かった場合」の処理がうまく行きません。

判定はPLSQLで定義してある変数で、明示カーソルを使用して取得を行っています。
(ID1:TEST1 ID2 =TEST2)
このときID1のデータは1つに絞り込めているのですが、ID2のデータが複数あり絞り込めていません。

その為「ID1 != ID2」の条件のとき
「ORA-01422: 完全フェッチが要求よりも多くの行を戻しました」とエラーが表示されてしまいます。

原因はID2が一つに絞り込めていないのが原因なのですが、力量不足故条件の絞込み方法を教えて頂きたいです

PLSQLは投稿したときに記述した内容とほぼ変わりないので、どこが不適切なのか指摘をお願い致します。

文面が不適切で分かりにくい所が多いと思われますが、ご教授の方宜しくお願い致します。

[ メッセージ編集済み 編集者: 始まり 編集日時 2009-03-30 11:19 ]
よっしー
大ベテラン
会議室デビュー日: 2007/05/17
投稿数: 143
投稿日時: 2009-03-30 12:46
始まりさん、こんにちは。

引用:

始まりさんの書き込み (2009-03-30 11:19) より:
INSERT、DELETEのみで処理を行う。UPDATEは使用不可、



下記と矛盾しませんか?

引用:
同期の内容はTEST1とTEST2データを比較して、下記の動作を行います。
・IDとKBN同一のデータが無ければINSERT
・IDとKBN同一のデータが有ればDATEを見て同期元の更新日時が新しければUPDATE



引用:
始まりさんの書き込み (2009-03-30 11:19) より:
後一番大事な事を書き忘れていましたがTEST1とTEST2のデータは比較するだけであって実際に同期を行うテーブルは別になります。


別ということはTEST1でもTEST2でもないということでしょうか?



上総
大ベテラン
会議室デビュー日: 2006/06/22
投稿数: 107
投稿日時: 2009-03-30 14:27
ちょっと2点程気になりましたので。

@提示されている条件とSQLについて

始まり様の書き込み(2009-03-25 18:23)より
引用:

TEST1のテーブルからIDとKBNにひもづく更新日時が最新のデータを取得する方法です。



上記の条件に対してのSQLですが、[b]TEST1の最新の日付を持つデータ[/b/を取得する内容
となっているので、条件と一致しません。
引用:

 CURSOR SYNCHRO IS
 SELECT DISTINCT ID, KBN
 FROM TEST1
 WHERE UP_DT IN (SELECT MAX(UP_DT) FROM TEST1);



ID・KBN毎の最新日付を取得する場合ですと、次のようなSQLになります。
コード:
    CURSOR      SYNCHRO
    IS
    SELECT      ALL
                ID,
                KBN,
                MAX ( UP_DT ) AS MAX_DATE
    FROM        TEST1
    GROUP BY    ID,
                KBN
    ORDER BY    ID ASC,
                KBN ASC;



APLSQLでの『SELECT INTO』について
始まり様の書き込み(2009-03-25 18:23)より
引用:

   EXIT WHEN SYNCHRO%NOTFOUND;
   SELECT DISTINCT ID,KBN,UP_DT INTO CT,YT,DT
   FROM TEST2@TEST
   WHERE UP_DT IN (SELECT MAX(UP_DT) FROM TEST2);



次に上記のSQLですが、PLSQLでは『複数件のレコードを返す』SQLは、カーソルを使用
しないとエラーとなります。
※『SELECT INTO』句は1行の結果を処理する為の命令で、複数件のレコードが返された
 時点でエラーが発生します。

 『カーソル』は複数件のレコードでも一件のレコードでも処理されますので、大抵の
 『SELECT』文は『カーソル』を使用する事でエラーは減らせるかと思います。
1

スキルアップ/キャリアアップ(JOB@IT)