CTS-KB
COBOL 4 / 5

COBOL × SQL 連携

#COBOL #SQL #DB2

対象: COBOL から SQL を使ってデータベースにアクセスする方法 DB2(AS/400 / z/OS)と GnuCOBOL + PostgreSQL の両方をカバー


目次

  1. COBOLとSQLの関係
  2. 埋め込みSQLの基本
  3. CRUD操作
  4. カーソル処理(複数行の取得)
  5. エラー処理(SQLCA)
  6. トランザクション制御
  7. GnuCOBOL + PostgreSQL(OCESQL)
  8. DB2/400(AS/400)での実装
  9. DB2 z/OS(メインフレーム)での実装
  10. 設計パターン

1. COBOLとSQLの関係

ファイル操作           SQL操作
──────────────        ──────────────
READ                  SELECT
WRITE                 INSERT
REWRITE               UPDATE
DELETE                DELETE
OPEN                  CONNECT / OPEN CURSOR
CLOSE                 DISCONNECT / CLOSE CURSOR
START (位置付け)       WHERE 条件
READ NEXT             FETCH NEXT

COBOLでSQLを使う方法は 埋め込みSQL(Embedded SQL) と呼ばれる。 COBOL のソースコード内に SQL 文を直接書く。

*> COBOLのコード内にSQLを埋め込む
EXEC SQL
    SELECT EMP_NAME, SALARY
    INTO :WS-EMP-NAME, :WS-SALARY
    FROM EMPLOYEE
    WHERE EMP_ID = :WS-EMP-ID
END-EXEC

コンパイルの流れ

コンパイルの流れ

.sqb

ソース

プリコンパイラ

(SQL → API呼出に変換)

.cob

COBOL

COBOLコンパイラ

(cobc)

実行ファイル


2. 埋め込みSQLの基本

2.1 ホスト変数(COBOL変数とSQLの橋渡し)

WORKING-STORAGE SECTION.

*> --- ホスト変数の宣言 ---
*> EXEC SQL ... END-EXEC の間で宣言すると
*> SQL文の中で :変数名 として使える

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
    01 WS-EMP-ID      PIC X(5).
    01 WS-EMP-NAME    PIC X(30).
    01 WS-SALARY      PIC S9(9)V99 COMP-3.
    01 WS-DEPT-CODE   PIC X(4).
EXEC SQL END DECLARE SECTION END-EXEC.

*> --- SQL通信領域(エラー情報) ---
EXEC SQL INCLUDE SQLCA END-EXEC.

ホスト変数の命名規則:

  • SQL文中では :WS-EMP-ID のようにコロン(:)を付ける
  • COBOL文中ではコロンなしで普通に使う

PICとSQLの型対応:

COBOL PICSQL型用途
PIC X(n)CHAR(n) / VARCHAR(n)文字列
PIC S9(n)V9(m) COMP-3DECIMAL(n+m, m)金額・数値
PIC S9(9) COMPINTEGER整数
PIC S9(18) COMPBIGINT大きな整数
PIC X(10)DATE日付(YYYY-MM-DD形式)
PIC X(8)DATE日付(YYYYMMDD形式)

2.2 NULL 値の扱い(インジケータ変数)

*> NULLかもしれないカラムにはインジケータ変数を使う
01 WS-PHONE         PIC X(15).
01 WS-PHONE-IND     PIC S9(4) COMP.
*>   0  = 値あり
*>  -1  = NULL
*>  > 0 = 値が切り捨てられた(元の長さ)

EXEC SQL
    SELECT PHONE
    INTO :WS-PHONE :WS-PHONE-IND
    FROM CUSTOMER
    WHERE CUSTOMER_ID = :WS-CUST-ID
END-EXEC

IF WS-PHONE-IND < 0
    DISPLAY "Phone is NULL"
ELSE
    DISPLAY "Phone: " WS-PHONE
END-IF

3. CRUD操作

3.1 SELECT(1行取得)

*> --- 1行だけ取得(INTO で直接受け取る) ---
MOVE "E001" TO WS-EMP-ID

EXEC SQL
    SELECT EMP_NAME, SALARY, DEPT_CODE
    INTO :WS-EMP-NAME, :WS-SALARY, :WS-DEPT-CODE
    FROM EMPLOYEE
    WHERE EMP_ID = :WS-EMP-ID
END-EXEC

EVALUATE SQLCODE
    WHEN 0
        DISPLAY "Found: " WS-EMP-NAME
    WHEN 100
        DISPLAY "Not found"
    WHEN OTHER
        DISPLAY "SQL error: " SQLCODE
END-EVALUATE

3.2 INSERT(追加)

EXEC SQL
    INSERT INTO EMPLOYEE
        (EMP_ID, EMP_NAME, SALARY, DEPT_CODE)
    VALUES
        (:WS-EMP-ID, :WS-EMP-NAME, :WS-SALARY, :WS-DEPT-CODE)
END-EXEC

IF SQLCODE NOT = 0
    DISPLAY "Insert error: " SQLCODE
END-IF

3.3 UPDATE(更新)

EXEC SQL
    UPDATE EMPLOYEE
    SET SALARY = :WS-NEW-SALARY
    WHERE EMP_ID = :WS-EMP-ID
END-EXEC

*> SQLERRD(3) = 更新された行数
DISPLAY "Updated " SQLERRD(3) " rows"

3.4 DELETE(削除)

EXEC SQL
    DELETE FROM EMPLOYEE
    WHERE EMP_ID = :WS-EMP-ID
END-EXEC

IF SQLERRD(3) = 0
    DISPLAY "No rows deleted"
END-IF

4. カーソル処理(複数行の取得)

SELECTの結果が複数行になる場合、カーソルを使って1行ずつ取得する。 COBOL のファイル操作(OPEN → READ UNTIL EOF → CLOSE)と同じ発想。

4.1 基本的なカーソル処理

*> --- 1. カーソルを宣言(DECLARE) ---
EXEC SQL
    DECLARE EMP-CURSOR CURSOR FOR
        SELECT EMP_ID, EMP_NAME, SALARY
        FROM EMPLOYEE
        WHERE DEPT_CODE = :WS-DEPT-CODE
        ORDER BY EMP_ID
END-EXEC

PROCEDURE DIVISION.
MAIN-PARA.
    MOVE "DEV" TO WS-DEPT-CODE

    *> --- 2. カーソルを開く(OPEN) ---
    EXEC SQL OPEN EMP-CURSOR END-EXEC

    *> --- 3. 1行ずつ取得(FETCH) ---
    PERFORM UNTIL SQLCODE NOT = 0
        EXEC SQL
            FETCH EMP-CURSOR
            INTO :WS-EMP-ID, :WS-EMP-NAME, :WS-SALARY
        END-EXEC

        IF SQLCODE = 0
            DISPLAY WS-EMP-ID " " WS-EMP-NAME " " WS-SALARY
        END-IF
    END-PERFORM

    *> --- 4. カーソルを閉じる(CLOSE) ---
    EXEC SQL CLOSE EMP-CURSOR END-EXEC.
ファイル操作との対比:

ファイル              カーソル
─────────            ─────────
OPEN INPUT ファイル   OPEN カーソル
READ ファイル         FETCH カーソル INTO ...
  AT END              SQLCODE = 100
CLOSE ファイル        CLOSE カーソル

4.2 更新用カーソル(FOR UPDATE)

*> 取得したレコードをそのまま更新する場合
EXEC SQL
    DECLARE UPD-CURSOR CURSOR FOR
        SELECT EMP_ID, SALARY
        FROM EMPLOYEE
        WHERE DEPT_CODE = :WS-DEPT-CODE
        FOR UPDATE OF SALARY         *> SALARY を更新する宣言
END-EXEC

EXEC SQL OPEN UPD-CURSOR END-EXEC

PERFORM UNTIL SQLCODE NOT = 0
    EXEC SQL
        FETCH UPD-CURSOR
        INTO :WS-EMP-ID, :WS-SALARY
    END-EXEC

    IF SQLCODE = 0
        *> 10% 昇給
        COMPUTE WS-NEW-SALARY = WS-SALARY * 1.10

        EXEC SQL
            UPDATE EMPLOYEE
            SET SALARY = :WS-NEW-SALARY
            WHERE CURRENT OF UPD-CURSOR  *> 現在のカーソル位置
        END-EXEC
    END-IF
END-PERFORM

EXEC SQL CLOSE UPD-CURSOR END-EXEC
EXEC SQL COMMIT END-EXEC

5. エラー処理(SQLCA)

5.1 SQLCA の構造

EXEC SQL INCLUDE SQLCA END-EXEC
*> これで以下の変数が自動的に定義される

*> SQLCODE: 最も重要なエラーコード
*>     0   = 正常
*>   100   = データなし(NOT FOUND)
*>   < 0   = エラー
*>   > 0   = 警告

*> SQLERRD(3): 処理された行数
*> SQLSTATE: 5桁の標準エラーコード('00000' = 正常)

5.2 SQLCODE の主要な値

SQLCODESQLSTATE意味
000000正常終了
10002000データなし(NOT FOUND)
-80323505一意制約違反(重複キー)
-81121000SELECT結果が複数行(INTO使用時)
-20442704テーブルが存在しない
-20642703カラムが存在しない
-53023503外部キー制約違反
-91140001デッドロック(ロック競合)
-90457011リソース不足

5.3 標準的なエラーチェックパターン

*> --- 全てのSQL文の後にチェックする ---
PERFORM CHECK-SQL-PARA

*> --- エラーチェック段落 ---
CHECK-SQL-PARA.
    EVALUATE TRUE
        WHEN SQLCODE = 0
            CONTINUE                    *> 正常
        WHEN SQLCODE = 100
            SET WS-NOT-FOUND TO TRUE    *> データなし
        WHEN SQLCODE = -803
            SET WS-DUPLICATE TO TRUE    *> 重複キー
        WHEN SQLCODE = -911
            *> デッドロック → リトライ
            DISPLAY "Deadlock detected, retrying..."
            EXEC SQL ROLLBACK END-EXEC
            SET WS-RETRY TO TRUE
        WHEN SQLCODE < 0
            *> その他のエラー
            DISPLAY "SQL error: " SQLCODE
            DISPLAY "SQLSTATE : " SQLSTATE
            SET WS-SQL-ERROR TO TRUE
    END-EVALUATE.

5.4 WHENEVER(自動エラーハンドリング)

*> SQL文でエラーが発生したら自動的に指定の処理を行う
EXEC SQL
    WHENEVER SQLERROR GO TO SQL-ERROR-PARA
END-EXEC

EXEC SQL
    WHENEVER NOT FOUND CONTINUE
END-EXEC

*> 以降の全SQL文でSQLERROR時にSQL-ERROR-PARAへジャンプ
条件動作オプション意味
SQLERRORSQLCODE < 0エラー発生時
SQLWARNINGSQLCODE > 0 and != 100警告発生時
NOT FOUNDSQLCODE = 100データなし
動作意味
GO TO 段落名指定段落へジャンプ
CONTINUE何もしない(次の文を実行)
STOPプログラムを停止

6. トランザクション制御

*> --- COMMIT: 変更を確定 ---
EXEC SQL COMMIT END-EXEC

*> --- ROLLBACK: 変更を取消 ---
EXEC SQL ROLLBACK END-EXEC

*> --- SAVEPOINT: 中間セーブポイント ---
EXEC SQL SAVEPOINT SP1 END-EXEC

*> ... 処理 ...

*> セーブポイントまで戻す(全部は戻さない)
EXEC SQL ROLLBACK TO SAVEPOINT SP1 END-EXEC

バッチ処理でのCOMMIT間隔

*> 大量データ処理では定期的にCOMMITする
77 WS-COMMIT-INTERVAL  PIC 9(5) VALUE 1000.
77 WS-COMMIT-COUNT     PIC 9(5) VALUE 0.

PROCESS-LOOP.
    EXEC SQL
        FETCH CURSOR1 INTO :WS-DATA
    END-EXEC

    IF SQLCODE = 0
        PERFORM BUSINESS-LOGIC-PARA

        EXEC SQL
            INSERT INTO OUTPUT_TABLE VALUES (:WS-RESULT)
        END-EXEC

        ADD 1 TO WS-COMMIT-COUNT
        IF WS-COMMIT-COUNT >= WS-COMMIT-INTERVAL
            EXEC SQL COMMIT END-EXEC
            MOVE 0 TO WS-COMMIT-COUNT
            DISPLAY "Committed at " WS-TOTAL-COUNT " records"
        END-IF
    END-IF.

7. GnuCOBOL + PostgreSQL(OCESQL)

7.1 環境構築

GnuCOBOL で PostgreSQL に接続するには OCESQL(Open COBOL ESQL)プリコンパイラを使う。

# OCESQL のインストール(Ubuntu/WSL)
sudo apt install libpq-dev
# OCESQL は別途ソースからビルドが必要
# https://github.com/opensourcecobol/Open-COBOL-ESQL

# または、GnuCOBOL 用の ODBC 連携も可能
sudo apt install unixodbc-dev odbc-postgresql

7.2 接続と切断

*> ソースファイルの拡張子は .sqb(SQL COBOLの慣例)

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
    01 WS-DBNAME    PIC X(30) VALUE "payroll_db".
    01 WS-USER      PIC X(30) VALUE "dbuser".
    01 WS-PASSWORD  PIC X(30) VALUE "dbpass".
EXEC SQL END DECLARE SECTION END-EXEC.

EXEC SQL INCLUDE SQLCA END-EXEC.

PROCEDURE DIVISION.
MAIN-PARA.
    *> データベースに接続
    EXEC SQL
        CONNECT :WS-USER
        IDENTIFIED BY :WS-PASSWORD
        USING :WS-DBNAME
    END-EXEC

    IF SQLCODE NOT = 0
        DISPLAY "Connect error: " SQLCODE
        STOP RUN
    END-IF

    DISPLAY "Connected to " WS-DBNAME

    *> ... 処理 ...

    *> 切断
    EXEC SQL DISCONNECT ALL END-EXEC
    STOP RUN.

7.3 コンパイル手順

# 1. プリコンパイル(.sqb → .cob に変換)
ocesql salary-db.sqb salary-db.cob

# 2. COBOLコンパイル(PostgreSQLライブラリをリンク)
cobc -x -free -o salary-db salary-db.cob \
    -L/usr/lib -lpq -locesql

# 3. 実行
./salary-db

7.4 完全なサンプル

*> ファイル: salary-db.sqb
*> GnuCOBOL + PostgreSQL での給与データ操作
IDENTIFICATION DIVISION.
    PROGRAM-ID. SALARY-DB.

DATA DIVISION.
WORKING-STORAGE SECTION.

EXEC SQL BEGIN DECLARE SECTION END-EXEC.
    01 WS-DBNAME      PIC X(30) VALUE "payroll_db".
    01 WS-USER        PIC X(30) VALUE "dbuser".
    01 WS-PASSWORD    PIC X(30) VALUE "dbpass".
    01 WS-EMP-ID      PIC X(5).
    01 WS-EMP-NAME    PIC X(30).
    01 WS-SALARY      PIC S9(9)V99.
    01 WS-DEPT        PIC X(8).
EXEC SQL END DECLARE SECTION END-EXEC.

EXEC SQL INCLUDE SQLCA END-EXEC.

77 WS-DISP-SALARY    PIC ZZZ,ZZZ,ZZ9.99.

PROCEDURE DIVISION.
MAIN-PARA.
    *> 接続
    EXEC SQL
        CONNECT :WS-USER
        IDENTIFIED BY :WS-PASSWORD
        USING :WS-DBNAME
    END-EXEC

    *> テーブル作成(初回のみ)
    EXEC SQL
        CREATE TABLE IF NOT EXISTS EMPLOYEE (
            EMP_ID    CHAR(5) PRIMARY KEY,
            EMP_NAME  CHAR(30),
            SALARY    DECIMAL(11,2),
            DEPT      CHAR(8)
        )
    END-EXEC

    *> データ挿入
    MOVE "E001"         TO WS-EMP-ID
    MOVE "TANAKA TARO"  TO WS-EMP-NAME
    MOVE 250000.00      TO WS-SALARY
    MOVE "DEVELOP"      TO WS-DEPT

    EXEC SQL
        INSERT INTO EMPLOYEE VALUES
        (:WS-EMP-ID, :WS-EMP-NAME, :WS-SALARY, :WS-DEPT)
    END-EXEC

    *> 検索
    MOVE "E001" TO WS-EMP-ID
    EXEC SQL
        SELECT EMP_NAME, SALARY
        INTO :WS-EMP-NAME, :WS-SALARY
        FROM EMPLOYEE
        WHERE EMP_ID = :WS-EMP-ID
    END-EXEC

    IF SQLCODE = 0
        MOVE WS-SALARY TO WS-DISP-SALARY
        DISPLAY WS-EMP-ID ": " WS-EMP-NAME
            " Salary=" WS-DISP-SALARY
    END-IF

    EXEC SQL COMMIT END-EXEC
    EXEC SQL DISCONNECT ALL END-EXEC
    STOP RUN.

8. DB2/400(AS/400)での実装

8.1 AS/400 の特徴

AS/400 では物理ファイル(PF)がそのまま DB2 のテーブルとして使える。COBOLからはファイル操作でもSQL操作でもアクセスできる。

AS/400 のファイル = DB2 テーブル

COBOL
├── ファイル操作(READ/WRITE) → 物理ファイル(PF)
└── 埋め込みSQL(SELECT等)   → 同じ物理ファイル

8.2 AS/400 固有のSQL文

*> AS/400 ではライブラリ.ファイル でテーブルを指定
EXEC SQL
    SELECT *
    FROM PAYROLL/EMPLOYEE
    WHERE DEPT = :WS-DEPT
END-EXEC

*> コミットメントコントロール
*> CL コマンドで事前に設定が必要:
*>   STRCMTCTL LCKLVL(*CHG)

EXEC SQL COMMIT END-EXEC
EXEC SQL ROLLBACK END-EXEC

8.3 コンパイル

/* AS/400 でのコンパイル (CL コマンド) */
CRTSQLCBL OBJ(MYLIB/MYPGM)
          SRCFILE(MYLIB/QCBLLESRC)
          SRCMBR(MYPGM)
          COMMIT(*CHG)
          OPTION(*EVENTF)

/* 手順:
   1. CRTSQLCBL でプリコンパイル + コンパイルを一括実行
   2. SQL文がDB2 APIコールに変換される
   3. COBOLプログラムオブジェクトが生成される */

9. DB2 z/OS(メインフレーム)での実装

9.1 プリコンパイルとバインド

9.1 プリコンパイルとバインド

1. プリコンパイル (DSNHPC)

1. プリコンパイル (DSNHPC)

2. COBOLコンパイル

3. リンク

4. バインド (BIND)

.sqb ソース

.cob

DBRM

(Database Request Module)

オブジェクト

ロードモジュール

パッケージ / プラン

(DB2側の実行計画)

9.2 JCL でのコンパイル

//SQLCOBOL EXEC DSNHCOB2,MEM=MYPGM,
//         PARM.PC='HOST(IBMCOB),APOST'
//PC.DBRMLIB DD DSN=MY.DBRM.LIB(MYPGM),DISP=SHR
//PC.SYSLIB  DD DSN=MY.COPY.LIB,DISP=SHR
//PC.SYSIN   DD DSN=MY.SOURCE.LIB(MYPGM),DISP=SHR
//*
//BIND     EXEC PGM=IKJEFT01
//SYSTSIN  DD *
  DSN SYSTEM(DB2P)
  BIND PACKAGE(MYPKG) MEMBER(MYPGM) -
       ISOLATION(CS) VALIDATE(BIND)
  END
/*

9.3 CICS でのオンラインSQL

*> CICS環境ではEXEC CICSとEXEC SQLが共存する
EXEC SQL
    SELECT BALANCE
    INTO :WS-BALANCE
    FROM ACCOUNT
    WHERE ACCOUNT_NO = :WS-ACCOUNT-NO
END-EXEC

IF SQLCODE NOT = 0
    EXEC CICS ABEND ABCODE('SQLE') END-EXEC
END-IF

*> CICS のトランザクション制御
EXEC CICS SYNCPOINT END-EXEC          *> COMMIT
EXEC CICS SYNCPOINT ROLLBACK END-EXEC *> ROLLBACK

10. 設計パターン

10.1 DAO パターン(データアクセスの分離)

業務ロジックとSQLを分離する。

10.1 DAO パターン(データアクセスの分離)

データアクセス層

業務ロジック層

CALL

CALL

salary-calc.cob

SQLを直接書かない

EMP-DAO-READ

employee-dao.cob

EMP-DAO-UPDATE

employee-dao.cob

DB2 /

PostgreSQL

*> employee-dao.cob(データアクセス層)
*> SQL文はここに集約する

PROCEDURE DIVISION
    USING LK-ACTION LK-EMP-RECORD LK-RESULT.

DAO-MAIN.
    EVALUATE LK-ACTION
        WHEN "READ"
            PERFORM DAO-READ-PARA
        WHEN "INSERT"
            PERFORM DAO-INSERT-PARA
        WHEN "UPDATE"
            PERFORM DAO-UPDATE-PARA
        WHEN "DELETE"
            PERFORM DAO-DELETE-PARA
    END-EVALUATE
    GOBACK.

DAO-READ-PARA.
    EXEC SQL
        SELECT EMP_NAME, SALARY, DEPT
        INTO :LK-EMP-NAME, :LK-SALARY, :LK-DEPT
        FROM EMPLOYEE
        WHERE EMP_ID = :LK-EMP-ID
    END-EXEC
    MOVE SQLCODE TO LK-RESULT-CODE.

10.2 SQL vs ファイル操作の使い分け

基準ファイル操作SQL
データ量全件順次処理条件付き抽出
アクセスパターン先頭から順番に処理ランダムアクセス
結合マッチング処理を自分で書くJOIN で結合
集計PERFORM でループして集計GROUP BY, SUM
移植性COBOL標準(どこでも動く)DB依存(DB2, PostgreSQL…)
性能順次処理は高速インデックスがあれば高速
適するもの大量バッチ、帳票マスタ検索、条件付き処理

10.3 環境ごとのSQL対応

項目GnuCOBOL + PostgreSQLDB2/400DB2 z/OS
プリコンパイラOCESQLCRTSQLCBL内蔵DSNHPC
接続方法CONNECT文暗黙的(同一システム)CONNECT or PLAN
コミットEXEC SQL COMMITEXEC SQL COMMITEXEC SQL COMMIT or CICS SYNCPOINT
ソース拡張子.sqbソースメンバソースメンバ
PostgreSQL型DB2/400型DB2型

関連記事