- Tác giả

- Name
- Nguyễn Đức Xinh
- 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ảnCOMPUTE– 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 đó:
sourcecó thể là literal, biến, field trong grouptargetlà 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ặcwkSqlIdx++;
Ư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
COMPUTEcho 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ằngNOT =– 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:
ANDORNOT
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 + BC = A - BC = A * BC = A / B(dùngCOMPUTEhoặcDIVIDE)
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 >= 10thì áp dụngDISCOUNT-RATE = 0.1, ngược lại0.0 - Tính:
TOTAL = UNIT-PRICE * QUANTITYDISCOUNT-AMOUNT = TOTAL * DISCOUNT-RATEFINAL-AMOUNT = TOTAL - DISCOUNT-AMOUNT
Gợi ý:
- Dùng
COMPUTEcho các expression - Dùng
IFvớ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ỗiWS-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.
