Pro*Cコーディング基礎
0.更新履歴
- 1993.12.21 初版(MacWriteII版)
- 1997.01.31 HTML化(タイトルを変えた)
- 2000.09.13 レイアウトを奇麗に?しました.
1.DBを使用する為の定義
/* DBの返却値を設定しておくと便利*/
#define DB_SUCCESS 0
#define DB_NOTFOUND 1403
#define DB_BUSY (-54)
#define SETLEN(X) X.len = strlen(X.arr)
#define SETNULL(X) X.arr[X.len]=0
/* DBの返却値を設定しておくと便利*/ #define DB_SUCCESS 0 #define DB_NOTFOUND 1403 #define DB_BUSY (-54) #define SETLEN(X) X.len = strlen(X.arr) #define SETNULL(X) X.arr[X.len]=0 |
|
|
SETLEN |
VARCHAR型の文字列長を調べその値を「x.len」に設定する |
SETNULL |
DB上では文字列データ等に制御文字(\0)等を持たないので文字列を確定する為に「x.len」に格納してある数値番目の配列にヌル文字を設定する。 |
/* DBをアクセスする為に必要なエリアを定義する。*/ EXEC SQL BEGIN DECLARE SECTION; /* DB access initialize using field */ LOCAL VARCHAR uid[20]; /* DB access user id. */ LOCAL VARCHAR pwd[20]; /* DB access user passwd*/ /* DB update absolute value field */ LOCAL VARCHAR DB_HOSTNAME[9]; /* Hostname */ LOCAL VARCHAR DB_PGMID[9]; /* this program id */ /* DB access field */ LOCAL VARCHAR DB_BDBCD[9]; /**/ LOCAL VARCHAR DB_BDBCYMD[7]; /**/ LOCAL VARCHAR DB_BDBCHM[7]; /**/ LOCAL VARCHAR DB_BDBIPGM[9]; /**/ LOCAL VARCHAR DB_BDBITRN[9]; /**/ EXEC SQL END DECLARE SECTION; |
BEGIN
とEND
の間にフィールドの定義を行う。 このエリアはグローバルである必要は無い。
フィールド定義では、たとえばDBに3文字(char(3))で登録してある場合には、NULL文字分だけ+1して4文字定義する必要がある。
EXEC SQL INCLUDE sqlca.h; |
DBとプログラム間の通信を行なうエリア。 アクセス情報(エラー等)を取得する事ができる。(Sql Communication
Area
の略)
2.DBアクセス時のエラー処理
/* エラー処理 */ switch(sqlca.sqlcode) { case DB_SUCCESS: EXEC SQL COMMIT WORK; break; case DB_NOTFOUND: printf("Not Foundエn"); break; dafault: printf("残念ながらDBに異常がありました。エn"); printf("ROLL BACKしますので御了承ください。エn"); printf("そしてシステム管理者に連絡してください。エn"); printf("sqlcode=(%d)エn",sqlca.sqlcode); printf("sqlmsg =(%s)エn",sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK WORK RELEASE; exit(FALSE); } /* end of switch () */ |
正常に処理が終わった段階でCOMMIT
する。 これを行なう事で内容がデータベース内で反映される。
sqlca
には、色々な情報を持っている場合があるがとりあえず次のような物を知っていれば良いでしょう。
|
|
sqlca.sqlcode |
ORACLEのエラーコードが設定されている。 これを判定する事で実行したDBアクセスの処理結果が判定出来る。 |
sqlca.sqlerrm.sqlerrmc |
ORACLEのエラーコードに対するメッセージが文字列として設定されている。 これを読めば簡単なエラーであると解決できる(ハズ) |
DBのアクセスで通常以外のエラーが発生された場合、たとえば更新処理中に何か不具合があった場合に、何処まで更新されたか分からないので、その処理を実行する直前まで戻しておく必要があります。
この様な場合に「ROLLBACK
」を行ないます。
ROLLBACK
は、一番最近COMMIT
して時点までDBの情報を戻します。
最後にCOMMIT
が実行されているという事は、そこまでの情報は保証されているという事です。
3.DB検索処理
/* DBを検索して取り込む */
strcpy(DB_ESORTKEY.arr,"KEY001");
EXEC SQL SELECT
ESBSYSID, ELISTID0,EPRTCENT
INTO
DB_ESBSYSID, DB_ELISTID0,DB_EPRTCENT
FROM TDSNNOD0
WHERE ESORTKEY = :DB_ESORTKEY;
SETNULL(DB_ESBSYSID);
SETNULL(DB_ELISTID0);
SETNULL(DB_EPRTCENT);
/* DBを検索して取り込む */ strcpy(DB_ESORTKEY.arr,"KEY001"); EXEC SQL SELECT ESBSYSID, ELISTID0,EPRTCENT INTO DB_ESBSYSID, DB_ELISTID0,DB_EPRTCENT FROM TDSNNOD0 WHERE ESORTKEY = :DB_ESORTKEY; SETNULL(DB_ESBSYSID); SETNULL(DB_ELISTID0); SETNULL(DB_EPRTCENT); |
この例では、2つの項目を条件によりDBから検索するものである。
SETNULL()
を使用しているのは、DBより読み込まれた直後であると文字列の終了が確定されていない為である。 これは、次のような弊害を生む。
- ABCというフィールドを持ってくる場合
- 1回目の処理で「123456789」を持ってきた。
- 2回目の処理で「9876」を持ってきた。
SETNULL()
で終点を確定していないと、2回目の処理で取得してABCというフィールドの中身は、前に読み込まれたエリアに上書きされて「987656789」となってしまう。
キーがユニークとなっているものであると前提し、そのキーがあらかじめ設定されているという前提で検索することが出来る。(つまり該当する件数が一件しか無いといえる場合のみ)
該当するDBレコードが複数ある場合には、それぞれINTOで取り込むエリアを配列で取っておく必要があるが、これを行なわないとメモリ領域を破壊してしまう。
しかし、配列を取っていたとしても最大件数が固定である場合以外には、これは推奨出来ない。 たとえば「現在10件程だから100程配列をとっておけばいいや」などと考えている場合に、後から追加もおこりうるDBの場合、いつかプログムが異常終了する可能性がある。
4.DB更新処理
/* 更新処理 */
/* 排他制御を行う*/
EXEC SQL
SELECT
ESBSYSID,ELISTIDO,EPRTCENT
INTO
DB_ESBSYSID, DB_ELISTID0, DB_EPRTCENT
FROM TDSNNOD0
WHERE ESORTKEY = :DB_ESORTKEY
FOR UPDATE OF ALL;
/* 更新処理 */ /* 排他制御を行う*/ EXEC SQL SELECT ESBSYSID,ELISTIDO,EPRTCENT INTO DB_ESBSYSID, DB_ELISTID0, DB_EPRTCENT FROM TDSNNOD0 WHERE ESORTKEY = :DB_ESORTKEY FOR UPDATE OF ALL; |
更新を行なうレコードに対してロックをかけます。 この事を排他制御と言います。 この例の場合ALLと指定してあるので、このレコードに存在するフィールド全項目をロックするという事です。
「限定されたフィールドのみ」ロックするには、ここにDBのフィールド名を設定します。
/* 更新を行う */ EXEC SQL UPDATE TDSNNOD0 SET BDBUYMD = TO_CHAR(SYSDATE,'YYMMDD'), BDBUHMS = TO_CHAR(SYSDATE,'HH24MISS'), ESBSYSID = :DB_ESBSYSID, ELISTID0 = :DB_ELISTID0, EFIXDSN0 = :DB_EFIXDSN0, ERENNO00 = :DB_ERENNO00, EPRTCENT = :DB_EPRTCENT, WHERE ESORTKEY = :DB_ESORTKEY; |
6.排他処理とは
DBは、一人だけが使用していると限られていない物です。 そこで自分が更新しようとしているデータを、他人が何処かで更新しようとたくらんでいる場合もあります。
それを一時的に出来なくする処理を「排他制御」と言います。
SQLでは、 ロックを行なった後にUPDATEを行ないCOMMITを発行すると排他制御は解除されます。
厳密に言えば、ここのサンプルでは無かったのですが、「ロック出来たか」の処理コードの判定も必要でしょう。
7.追加(挿入)処理
/* 追加処理 */ EXEC SQL INSERT INTO TDSNNOD0 (ESBSYSID, ELISTID0, EFIXDSN0, ERENNO00, EOVERRY1, EOVERRY2, ELISTNM0, EPRTJOB0, EPRTJOB1, EPRTSHEL, EPRTCENT, ESORTKEY, ) VALUES (:DB_ESBSYSID, :DB_ELISTID0, :DB_EFIXDSN0, :DB_ERENNO00, :DB_EOVERRY1, :DB_EOVERRY2, :DB_ELISTNM0, :DB_EPRTJOB0, :DB_EPRTJOB1, :DB_EPRTSHEL, :DB_EPRTCENT, :DB_ESORTKEY, ); |
8.削除処理
/* 削除処理 */ EXEC SQL DELETE FROM TDSNNOD0 WHERE ESORTKEY = :DB_ESORTKEY; |
9.カーソルを使った検索処理
/* カーソルの定義 */ EXEC SQL DECLARE C1 CURSOR FOR SELECT EPRTCENT, ESORTKEY FROM TDSNNOD0 ORDER BY ESORTKEY; EXEC SQL OPEN C1; /* Headder */ fprintf(printer,"KEY SHELL\n\n"); while(loop==TRUE) { EXEC SQL FETCH C1 INTO DB_EPRTCENT, DB_ESORTKEY; switch(sqlca.sqlcode) { case DB_SUCCESS: SETNULL(DB_EPRTCENT); SETNULL(DB_ESORTKEY); fprintf(printer,"%4s %6s \n", DB_ESORTKEY.arr, DB_EPRTCENT.arr); break; case DB_NOTFOUND: EXEC SQL CLOSE C1; loop=FALSE; PRTclose(printer); break; default : EXEC SQL CLOSE C1; EXEC SQL ROLLBACK WORK RELEASE; loop=FALSE; PRTclose(printer); break; } /* end of switch() */ } /* end of while() */ |
このプログラムでは、カーソルを使用し、一件づつ取り出したDBのフィールドを帳票イメージとしてファイルに書き出すプログラムです。
カーソルを使用した処理を行なった後には、必ずCLOSEする様にしてください。
カーソルを実行すると、該当するDBを一度にORACLEの制御するエリアに持ってくるのですが、CLOSEしないとこれが残ったままになり、「容量が足りないエラー」みたいなものが発生します。