Site logo
Tác giả
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Ngày xuất bản
Ngày xuất bản

Toán tử và biểu thức trong COBOL – Hướng dẫn chi tiết cho backend developer

Toán tử và biểu thức trong COBOL là gì?

Sau khi đã nắm biến và kiểu dữ liệu ở bài 002, bước tiếp theo để có thể viết được logic thực sự là hiểu toán tử (operator) và biểu thức (expression) trong COBOL.

Khác với nhiều ngôn ngữ hiện đại (Java, C#, Python) dùng ký hiệu +, -, *, / trực tiếp trong expression, COBOL ưu tiên dùng câu lệnh dạng "động từ":

  • MOVE – gán giá trị
  • ADD, SUBTRACT, MULTIPLY, DIVIDE – các phép toán số học cơ bản
  • COMPUTE – phép tính expression linh hoạt hơn (gần với C/Java style)

Ngoài ra còn có:

  • Toán tử so sánh trong IF, EVALUATE: =, >, <, >=, <=, NOT =
  • Các toán tử logic: AND, OR, NOT
  • Hằng số đặc biệt (figurative constants): ZERO, ZEROES, SPACE, SPACES, HIGH-VALUE, LOW-VALUE, ALL "*", ...

Bài này tập trung vào:

  • Cách viết và đọc các câu lệnh toán học/logic trong COBOL
  • So sánh với cách viết trong ngôn ngữ hiện đại để developer backend dễ mapping
  • Liên hệ với code thực tế như RZZBSQLN1.PCO (batch chương trình enterprise)

MOVE – Gán giá trị trong COBOL

Cú pháp cơ bản

MOVE source TO target.

Trong đó:

  • source có thể là literal, biến, field trong group
  • target là biến/field sẽ nhận giá trị

Ví dụ:

       01 WS-NAME        PIC X(20).
       01 WS-MESSAGE     PIC X(50).
       01 WS-COUNT       PIC 9(4).

       PROCEDURE DIVISION.
           MOVE "Nguyen Van A" TO WS-NAME.
           MOVE 0              TO WS-COUNT.
           MOVE "Hello COBOL"  TO WS-MESSAGE.
           STOP RUN.

Trong RZZBSQLN1.PCO, bạn sẽ thấy rất nhiều lệnh dạng:

MOVE  "INP1-OPEN-RTN"       TO    DSP_PROC.
MOVE  ZERO                  TO    COMPLETION-CODE.
MOVE  SPACE                 TO    BCMO-2.
MOVE  PROGRAM_ID            TO    DSP_PROGID.

Mapping mindset backend dev:

  • MOVE "abc" TO WS-NAME. ~ WS_NAME = "abc";
  • MOVE ZERO TO WS-COUNT. ~ wsCount = 0;

Gán group item

Với group item, MOVE sẽ sao chép cả block dữ liệu:

       01 CUSTOMER-SRC.
          05 CUST-ID      PIC 9(5).
          05 CUST-NAME    PIC X(30).

       01 CUSTOMER-DST.
          05 CUST-ID      PIC 9(5).
          05 CUST-NAME    PIC X(30).

       PROCEDURE DIVISION.
           MOVE 12345             TO CUST-ID OF CUSTOMER-SRC.
           MOVE "Nguyen Van A"   TO CUST-NAME OF CUSTOMER-SRC.

           MOVE CUSTOMER-SRC      TO CUSTOMER-DST.
           STOP RUN.

Cẩn thận: cấu trúc source và target nên có cùng layout để tránh behaviour khó đoán.


Các phép toán số học: ADD, SUBTRACT, MULTIPLY, DIVIDE

ADD – cộng

Cú pháp dạng cơ bản

ADD a TO b.

Tương đương: b = b + a.

Ví dụ:

       01 WS-COUNT       PIC 9(4) VALUE 0.

       PROCEDURE DIVISION.
           ADD 1 TO WS-COUNT.
           ADD 5 TO WS-COUNT.
           STOP RUN.

Cú pháp với GIVING

ADD a b GIVING c.

Tương đương: c = a + b (không thay đổi a, b).

Ví dụ tính tổng lương:

       01 WS-BASIC-SAL    PIC 9(7)V99.
       01 WS-ALLOWANCE    PIC 9(5)V99.
       01 WS-TOTAL-SAL    PIC 9(7)V99.

       PROCEDURE DIVISION.
           MOVE 0001500000 TO WS-BASIC-SAL.   *> 15000.00
           MOVE 0000025000 TO WS-ALLOWANCE.   *>   250.00

           ADD WS-BASIC-SAL WS-ALLOWANCE
               GIVING WS-TOTAL-SAL.
           DISPLAY "Total: " WS-TOTAL-SAL.
           STOP RUN.

SUBTRACT – trừ

SUBTRACT a FROM b.

Tương đương: b = b - a.

       01 WS-BALANCE   PIC S9(7)V99.

       PROCEDURE DIVISION.
           MOVE 0005000000 TO WS-BALANCE.   *> 50000.00
           SUBTRACT 0000010000 FROM WS-BALANCE.  *> -100.00
           DISPLAY "Balance: " WS-BALANCE.
           STOP RUN.

Với GIVING:

SUBTRACT a FROM b GIVING c.

Tương đương: c = b - a.

MULTIPLY – nhân

MULTIPLY a BY b.

Tương đương: b = a * b.

       01 WS-UNIT-PRICE  PIC 9(3)V99.
       01 WS-QUANTITY    PIC 9(3).
       01 WS-TOTAL       PIC 9(5)V99.

       PROCEDURE DIVISION.
           MOVE 000012050 TO WS-UNIT-PRICE.  *> 120.50
           MOVE 3         TO WS-QUANTITY.

           MULTIPLY WS-UNIT-PRICE BY WS-QUANTITY
               GIVING WS-TOTAL.

           DISPLAY "Total: " WS-TOTAL.
           STOP RUN.

DIVIDE – chia

DIVIDE a INTO b GIVING c.

Tương đương: c = b / a.

       01 WS-SUM      PIC 9(5)V99.
       01 WS-COUNT    PIC 9(3).
       01 WS-AVG      PIC 9(3)V99.

       PROCEDURE DIVISION.
           MOVE 000030000 TO WS-SUM.     *> 300.00
           MOVE 3         TO WS-COUNT.

           DIVIDE WS-COUNT INTO WS-SUM
               GIVING WS-AVG.

           DISPLAY "AVG: " WS-AVG.
           STOP RUN.

Trong thực tế, các hệ thống enterprise hiện đại thường thích dùng COMPUTE cho biểu thức phức tạp hơn.


COMPUTE – biểu thức linh hoạt kiểu C/Java

COMPUTE cho phép viết expression tương đối giống C/Java:

COMPUTE target = expression.

Ví dụ đơn giản:

       01 WS-A         PIC 9(3) VALUE 10.
       01 WS-B         PIC 9(3) VALUE 5.
       01 WS-RESULT    PIC S9(4).

       PROCEDURE DIVISION.
           COMPUTE WS-RESULT = (WS-A + WS-B) * 2.
           DISPLAY "Result: " WS-RESULT.
           STOP RUN.

Trong RZZBSQLN1.PCO có đoạn:

COMPUTE WK_SQL_IDX = WK_SQL_IDX  +  1.

Mapping sang Java/C:

  • COMPUTE WK_SQL_IDX = WK_SQL_IDX + 1. ~ wkSqlIdx = wkSqlIdx + 1; hoặc wkSqlIdx++;

Ưu điểm của COMPUTE:

  • Viết được biểu thức dài, có +, -, *, /, dấu ngoặc
  • Gần với phong cách dev hiện đại → dễ đọc hơn khi biểu thức phức tạp

Khuyến nghị:

  • Dùng ADD/SUBTRACT/... cho logic đơn giản, khi codebase legacy đã dùng style này.
  • Dùng COMPUTE cho các tính toán mới, phức tạp hơn (nhưng vẫn giữ readable).

Toán tử so sánh trong COBOL

Các toán tử so sánh được dùng trong condition (IF/EVALUATE):

  • = – bằng
  • >, < – lớn hơn, nhỏ hơn
  • >=, <= – lớn hơn hoặc bằng, nhỏ hơn hoặc bằng
  • NOT = – khác

Ví dụ:

       IF WS-AGE >= 18
           DISPLAY "Adult"
       ELSE
           DISPLAY "Minor"
       END-IF.

Trong RZZBSQLN1.PCO, có các condition điển hình:

IF F_STS  NOT = "00"
    ... xử lý lỗi file ...
END-IF.

IF INP1_INPCNT > 0
    PERFORM SQL-EXEC-RTN THRU SQL-EXEC-EXT
END-IF.

Mapping mindset:

  • IF F_STS NOT = "00" ~ if (!"00".equals(fSts)) { ... }
  • IF INP1_INPCNT > 0 ~ if (inp1Inpcnt > 0) { ... }

Ngoài ra, COBOL còn có class condition như IS NUMERIC, IS ALPHABETIC (sẽ phù hợp cho bài nâng cao hơn).


Toán tử logic: AND, OR, NOT

Khi kết hợp nhiều điều kiện, COBOL dùng:

  • AND
  • OR
  • NOT

Ví dụ:

       IF WS-AGE >= 18 AND WS-COUNTRY = "VN"
           DISPLAY "Adult in Vietnam"
       END-IF.

       IF NOT (F_STS = "00") OR ERR1_ERRCNT > 0
           DISPLAY "Has error"
       END-IF.

Trong batch enterprise, pattern thường gặp:

IF WK_BCMO_1 = ZERO OR
   BCMO-2    = SPACE
    GO TO CMD-GET-ERR
END-IF.

Đây là ví dụ trong RZZBSQLN1, kiểm tra xem command parameter có hợp lệ không.


Hằng số đặc biệt (figurative constants)

COBOL có một số figurative constants rất hay dùng:

Tên Ý nghĩa Ví dụ sử dụng
ZERO Giá trị 0 MOVE ZERO TO WS-COUNT.
ZEROES Tương tự ZERO (nhiều digit) MOVE ZEROES TO WS-SALARY.
SPACE Một ký tự space MOVE SPACE TO F_STS.
SPACES Nhiều space MOVE SPACES TO WS-NAME.
HIGH-VALUE Byte lớn nhất trong collating seq Ít dùng hơn, cho sort/compare
LOW-VALUE Byte nhỏ nhất Ít dùng hơn
ALL "*" Fill toàn bộ field bằng ký tự * MOVE ALL "*" TO DSP_MESSAGE1.

Trong RZZBSQLN1:

77  F_STS                       PIC  X(02) VALUE SPACE.
01  DSP_MESSAGE1.
    02  FILLER                  PIC  X(50) VALUE ALL "*".
...
MOVE ZERO TO EXEC_TRCCNT.
MOVE SPACE TO BCMO-2.

Khi refactor hoặc viết mới, bạn nên giữ style này để đồng bộ với codebase legacy.


So sánh cách viết expression: COBOL vs Java vs Python

Ý tưởng COBOL Java/C# Python
Gán giá trị MOVE 10 TO WS-AGE. age = 10; age = 10
Tăng biến đếm ADD 1 TO WS-COUNT.
COMPUTE WS-COUNT = WS-COUNT + 1.
count++;
count = count + 1;
count += 1
Cộng 2 số vào biến thứ 3 ADD A B GIVING C. c = a + b; c = a + b
Nếu status khác "00" IF F_STS NOT = "00" ... END-IF. if (!"00".equals(fSts)) {} if f_sts != "00":
Vòng lặp tăng index COMPUTE IDX = IDX + 1. idx++; idx += 1
Biểu thức phức tạp COMPUTE R = (A + B) * 2 / C. r = (a + b) * 2 / c; r = (a + b) * 2 / c

Khi đọc code COBOL, hãy luôn dịch mental model sang ngôn ngữ bạn quen dùng – điều này giúp hiểu nhanh logic trước khi quan tâm đến chi tiết PIC.


Ví dụ tổng hợp: Tính toán điểm trung bình và phân loại

Ví dụ nhỏ này kết hợp nhiều khái niệm trong bài:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SCORE-CALC.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WS-SCORE1        PIC 9(3).
       01 WS-SCORE2        PIC 9(3).
       01 WS-SCORE3        PIC 9(3).
       01 WS-TOTAL         PIC 9(4).
       01 WS-AVG           PIC 9(3)V9.
       01 WS-RANK          PIC X(10).

       PROCEDURE DIVISION.
           MOVE  80 TO WS-SCORE1.
           MOVE  90 TO WS-SCORE2.
           MOVE  75 TO WS-SCORE3.

           ADD WS-SCORE1 WS-SCORE2 WS-SCORE3
               GIVING WS-TOTAL.

           COMPUTE WS-AVG = WS-TOTAL / 3.

           IF WS-AVG >= 85
               MOVE "Excellent" TO WS-RANK
           ELSE
               IF WS-AVG >= 70
                   MOVE "Good" TO WS-RANK
               ELSE
                   MOVE "Average" TO WS-RANK
               END-IF
           END-IF.

           DISPLAY "TOTAL: " WS-TOTAL.
           DISPLAY "AVG  : " WS-AVG.
           DISPLAY "RANK : " WS-RANK.

           STOP RUN.

Bạn có thể mở rộng ví dụ:

  • Dùng EVALUATE để thay IF lồng nhau
  • Dùng PERFORM để tính tổng trong loop (khi có nhiều điểm hơn)

Bài tập thực hành

Bài 1 – Cộng, trừ, nhân, chia đơn giản

Yêu cầu:

  • Khai báo 3 biến numeric: A, B, C
  • Gán giá trị: A = 100, B = 25
  • Tính và hiển thị:
    • C = A + B
    • C = A - B
    • C = A * B
    • C = A / B (dùng COMPUTE hoặc DIVIDE)

Gợi ý:

  • Dùng ADD, SUBTRACT, MULTIPLY, DIVIDE ở phiên bản 1
  • Dùng COMPUTE ở phiên bản 2

Bài 2 – Tính chiết khấu đơn hàng

Yêu cầu:

  • Input: UNIT-PRICE (9(3)V99), QUANTITY (9(3))
  • Nếu QUANTITY >= 10 thì áp dụng DISCOUNT-RATE = 0.1, ngược lại 0.0
  • Tính:
    • TOTAL = UNIT-PRICE * QUANTITY
    • DISCOUNT-AMOUNT = TOTAL * DISCOUNT-RATE
    • FINAL-AMOUNT = TOTAL - DISCOUNT-AMOUNT

Gợi ý:

  • Dùng COMPUTE cho các expression
  • Dùng IF với toán tử so sánh >=

Bài 3 – Đếm số record và kiểm tra lỗi file (mô phỏng RZZBSQLN1)

Yêu cầu thiết kế WORKING-STORAGE:

  • WS-REC-COUNT (9(6)) – đếm số record đã xử lý
  • WS-ERR-COUNT (9(6)) – đếm lỗi
  • WS-FILE-STS (X(02)) – file status

Và logic (pseudo-code):

OPEN INPUT FILE
IF WS-FILE-STS NOT = "00" -> tăng WS-ERR-COUNT, hiển thị message, STOP RUN

LOOP:
  READ FILE
    AT END: exit loop
    NOT AT END:
      tăng WS-REC-COUNT
      nếu WS-FILE-STS NOT = "00" -> tăng WS-ERR-COUNT

DISPLAY tổng record, tổng lỗi
CLOSE FILE

Hãy viết các câu MOVE, ADD, IF tương ứng, ngay cả khi bạn chưa cài môi trường chạy COBOL.

Bài 4 – Viết lại một đoạn trong RZZBSQLN1 bằng pseudo-code hiện đại

Lấy đoạn:

COMPUTE WK_SQL_IDX = WK_SQL_IDX  +  1.
IF WK_SQL_IDX <= 50
    MOVE INP1_SQL TO WK_SQL(WK_SQL_IDX)
    DISPLAY INP1_SQL
END-IF.

Hãy viết lại bằng Java hoặc Python (pseudo-code) để đảm bảo bạn hiểu rõ logic.

Ví dụ (Java-style):

wkSqlIdx = wkSqlIdx + 1;
if (wkSqlIdx <= 50) {
    wkSql[wkSqlIdx] = inp1Sql;
    System.out.println(inp1Sql);
}

Kết luận

Trong bài này, bạn đã đi từ MOVE, ADD, SUBTRACT, MULTIPLY, DIVIDE đến COMPUTE, toán tử so sánh, toán tử logic, figurative constants – đủ để diễn tả phần lớn business logic số học và điều kiện trong một chương trình COBOL.

Kết hợp với kiến thức ở các bài trước về DATA DIVISION, PIC, group item, bạn đã có thể:

  • Đọc và hiểu hầu hết các câu lệnh xử lý số liệu trong code COBOL enterprise
  • Mapping nhanh sang tư duy Java/C#/Python để không bị choáng khi mở file dài
  • Chuẩn bị cho các bài tiếp theo về IF/EVALUATE, PERFORM loop, FILE SECTION, embedded SQL

Ở bài kế tiếp, chúng ta sẽ đi sâu vào Cấu trúc điều kiện trong COBOL (IF, EVALUATE) – cách viết logic rẽ nhánh rõ ràng, tránh lồng IF quá sâu, và cách hiểu block EVALUATE COMPLETION-CODE trong chương trình thực tế như RZZBSQLN1.