- Tác giả

- Name
- Nguyễn Đức Xinh
- Ngày xuất bản
- Ngày xuất bản
Vì sao trong COBOL có 01 trước tên biến? Hiểu đúng về Level Number và cấu trúc dữ liệu phân cấp
Vì sao trong COBOL có 01 trước tên biến?
Khi đọc code COBOL, bạn sẽ thường thấy cấu trúc như sau:
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).
Rất nhiều người mới học COBOL thắc mắc:
Tại sao lại có
01trướcCUSTOMER-SRCvàCUSTOMER-DST?
Nó có ý nghĩa gì? Có giống kiểu dữ liệu không?
Trong bài viết này, chúng ta sẽ phân tích rõ bản chất của 01, cách COBOL tổ chức dữ liệu, và tại sao điều này cực kỳ quan trọng trong hệ thống legacy.
1️⃣ 01 là gì trong COBOL?
01 là Level Number – mức cấp trong cấu trúc dữ liệu phân cấp của COBOL.
COBOL tổ chức dữ liệu theo dạng cây (hierarchical structure), không giống như kiểu struct/class hiện đại.
Ví dụ các level thường gặp:
| Level | Ý nghĩa |
|---|---|
| 01 | Record cấp cao nhất (root) |
| 05 | Field con của 01 |
| 10 | Field con của 05 |
| 77 | Biến đơn lẻ (không thuộc group) |
| 88 | Condition name (biến điều kiện) |
Trong bài 002 về biến và kiểu dữ liệu, bạn đã thấy level number dùng để khai báo group item và field con. Ở đây, ta zoom kỹ hơn vào vai trò của 01.
2️⃣ Cấu trúc bộ nhớ của ví dụ CUSTOMER-SRC và CUSTOMER-DST
Đoạn code:
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).
Tạo ra 2 record độc lập trong bộ nhớ:
CUSTOMER-SRC
├── CUST-ID (5 bytes)
└── CUST-NAME (30 bytes)
CUSTOMER-DST
├── CUST-ID (5 bytes)
└── CUST-NAME (30 bytes)
Mỗi record có kích thước:
CUST-ID: 5 ký tự numeric → 5 bytesCUST-NAME: 30 ký tự alphanumeric → 30 bytes
Tổng cộng: 35 bytes cho mỗi record.
Hai vùng nhớ này hoàn toàn tách biệt. 01 chính là root của mỗi vùng dữ liệu.
3️⃣ Vì sao phải là 01 chứ không phải 05 hay 10?
Trong COBOL:
01là level root – bắt đầu một cấu trúc dữ liệu độc lập- Các level khác (05, 10, 15…) phải nằm bên trong một level lớn hơn
Nếu bạn viết:
05 CUSTOMER-SRC.
→ Compile sẽ lỗi.
Vì 05 không thể đứng độc lập. Nó phải thuộc một 01 (hoặc một level thấp hơn đã khai báo trước đó).
Nói cách khác:
01~ định nghĩa ra một "record"/"struct" hoàn chỉnh05,10,15~ các field con bên trong record đó
Đây là lý do hầu hết record trong FILE SECTION hay WORKING-STORAGE đều bắt đầu bằng 01.
4️⃣ Tại sao hay thấy hai record giống nhau: SRC và DST?
Pattern phổ biến trong batch COBOL:
| Record | Vai trò |
|---|---|
CUSTOMER-SRC |
Record nguồn (input) |
CUSTOMER-DST |
Record đích (output) |
Ví dụ thực tế:
- Đọc file vào
CUSTOMER-SRC - Validate / chuẩn hóa dữ liệu
- Ghi ra
CUSTOMER-DST(có thể layout giống hoặc hơi khác, tùy hệ thống)
Kiểu thiết kế này giúp tách dữ liệu đầu vào và dữ liệu đã xử lý rõ ràng, tránh ghi đè lung tung.
5️⃣ Điều quan trọng: Group MOVE (MOVE cả record 01)
Trong đoạn code kinh điển:
MOVE CUSTOMER-SRC TO CUSTOMER-DST.
Đây không phải là copy từng field bằng tay.
Trong COBOL:
MOVE group-to-group = copy toàn bộ memory block theo layout đã khai báo.
Có thể hình dung tương đương với C:
memcpy(dst, src, sizeof(src));
Điều kiện để group MOVE an toàn:
- Hai structure phải có cùng layout (hoặc ít nhất là tương thích)
- Tổng kích thước (số byte) phải khớp hoặc bạn hiểu rõ mình đang chấp nhận truncate/garbage
Trong ví dụ này:
CUSTOMER-SRC= 35 bytesCUSTOMER-DST= 35 bytes
→ MOVE CUSTOMER-SRC TO CUSTOMER-DST là hợp lệ và an toàn.
Đây là một trong những pattern rất mạnh (và cũng rất nguy hiểm nếu dùng sai) trong COBOL: copy cả record chỉ với một lệnh MOVE.
6️⃣ Tại sao phải viết OF CUSTOMER-SRC khi gán từng field?
Xem đoạn code đầy đủ hơn:
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.
Ở đây có hai biến tên CUST-ID:
CUST-IDtrongCUSTOMER-SRCCUST-IDtrongCUSTOMER-DST
Nếu bạn chỉ viết:
MOVE 12345 TO CUST-ID.
Compiler sẽ báo lỗi (ambiguous reference) vì không biết bạn muốn gán vào field nào.
Do đó, bạn phải fully-qualify:
CUST-ID OF CUSTOMER-SRC- hoặc
CUST-ID OF CUSTOMER-DST
Pattern này rất quan trọng khi bạn có nhiều record với layout giống nhau trong cùng một chương trình.
7️⃣ So sánh với ngôn ngữ hiện đại (Java, PL/SQL)
Java
class Customer {
int custId;
String custName;
}
Customer src = new Customer();
Customer dst = new Customer();
src.custId = 12345;
src.custName = "Nguyen Van A";
// Copy toàn bộ object (giả sử có hàm copy)
copyCustomer(dst, src);
Ở đây:
Customer~ layout của record (giống phần sau01trong COBOL)src/dst~ hai biến instance khác nhau
PL/SQL
TYPE customer_rec IS RECORD (
cust_id NUMBER(5),
cust_name VARCHAR2(30)
);
src customer_rec;
dst customer_rec;
Trong mental model này:
- COBOL
01 CUSTOMER-SRC~ một instance củacustomer_rec - COBOL
01 CUSTOMER-DST~ một instance khác của cùng layout
Khác biệt là COBOL diễn tả trực tiếp layout bằng PIC + level number, thay vì dùng TYPE/class riêng.
8️⃣ Điểm nguy hiểm của group MOVE khi layout thay đổi
Giả sử bạn chỉnh sửa record destination:
01 CUSTOMER-DST.
05 CUST-ID PIC 9(5).
05 CUST-NAME PIC X(30).
05 CUST-STATUS PIC X(1).
Tức là:
CUSTOMER-SRC= 35 bytesCUSTOMER-DST= 36 bytes
Câu lệnh:
MOVE CUSTOMER-SRC TO CUSTOMER-DST.
Vẫn compile bình thường, nhưng:
- 35 bytes đầu của
CUSTOMER-DSTsẽ được overwrite bởiCUSTOMER-SRC - Byte cuối cùng (
CUST-STATUS) có thể giữ nguyên giá trị cũ hoặc garbage (tùy initial value)
Nếu struct chênh lệch theo hướng ngược lại (source dài hơn destination), dữ liệu có thể bị truncate.
Vì COBOL không type-safe như Java/C#, các lỗi kiểu này khó trace trong hệ thống lớn, đặc biệt khi data chạy qua nhiều batch job.
Best practice khi làm việc với legacy:
- Cực kỳ cẩn thận khi thay đổi layout của các record
01 - Tìm tất cả chỗ đang
MOVEgroup-to-group giữa các record này trước khi sửa
9️⃣ Tóm tắt nhanh về vai trò của 01
01 trong COBOL:
- Là level number cao nhất (root) của một cấu trúc dữ liệu
- Tạo ra một record độc lập trong bộ nhớ
- Cho phép sử dụng group MOVE để copy cả block dữ liệu một lần
- Là nền tảng để tổ chức dữ liệu phân cấp (01 → 05 → 10 ...)
Không có 01 → không có root structure → không tạo được record hoàn chỉnh.
Khi đọc code COBOL, mỗi lần thấy 01 bạn nên nghĩ ngay:
"À, đây là một record/struct – một đơn vị dữ liệu lớn".
🔟 Khi nào cần hiểu sâu về 01 và level number?
Bạn nên nắm rất rõ về 01 và level number nếu đang làm các công việc sau:
- Migrate COBOL sang PL/SQL / Java / dịch vụ microservice
- Refactor hệ thống legacy để tách module, chuẩn hóa data
- Debug batch program xử lý dữ liệu file/phát sinh lỗi mapping
- Làm data mapping giữa COBOL copybook và schema trong database hoặc message (JSON/AVRO)
Nhiều tool ETL, data integration, hoặc generator sẽ đọc COBOL copybook (dựa trên 01, 05, 10…) rồi sinh ra:
- Bảng database
- Struct trong Java/Scala (Spark)
- Schema trong Kafka/Avro
Nếu bạn không hiểu level number, rất khó kiểm soát được việc mapping có đúng hay không.
So sánh level number COBOL với struct/class hiện đại
| Khái niệm | COBOL | Java / C# / PL/SQL |
|---|---|---|
| Định nghĩa layout dữ liệu | 01 CUSTOMER ... 05 FIELD ... |
class Customer { ... } |
| Instance dữ liệu | Mỗi 01 là một instance |
Mỗi biến new Customer() |
| Field con | Level 05, 10, 15 | Thuộc tính trong class |
| Biến đơn lẻ không thuộc group | Level 77 | Biến global/local riêng lẻ |
| Biến điều kiện (flag logic) | Level 88 | Enum/const/boolean helper |
Bức tranh tổng thể:
- Level number là cách COBOL mô tả cây dữ liệu
01chính là gốc (root) của cây đó- Các kỹ thuật như group MOVE, REDEFINES, OCCURS… đều dựa trên cách bố trí bộ nhớ theo level number
Kết luận
01 không chỉ là một con số đứng trước tên biến trong COBOL.
Nó đại diện cho:
Cách COBOL tổ chức bộ nhớ, cách dữ liệu được copy, và cách toàn bộ hệ thống batch vận hành.
Hiểu được 01 là bước đầu tiên để:
- Đọc và hiểu các FILE SECTION / WORKING-STORAGE phức tạp
- Thiết kế data mapping chính xác khi migrate hệ thống
- Tự tin chỉnh sửa, refactor hoặc mở rộng chương trình COBOL mà không làm hỏng data layout
Nếu bạn đang trong quá trình đưa hệ thống COBOL sang kiến trúc hiện đại, các chủ đề tiếp theo nên đào sâu là:
- Sự khác biệt chi tiết giữa
01,77, và88 - Cách convert COBOL structure (copybook) sang PL/SQL RECORD, Java class, JSON schema
- Cách parse level structure để build tool tự động chuyển đổi từ COBOL sang format khác
Bạn muốn đào sâu theo hướng nào, hãy note lại – chúng ta có thể biến nó thành bài tiếp theo trong series về COBOL data structure.
