Обработка пакета транзакций
Разбираем, как COBOL-программа обрабатывает список транзакций одну за другой — основа любой ночной банковской обработки.
Пакетная обработка (batch processing) — это когда программа не ждёт, пока человек нажмёт кнопку, а сама проходит по большому списку операций подряд, без остановки, и обрабатывает каждую по одинаковым правилам.
Зачем это нужно банку
Представь: за день в банке прошло два миллиона операций — переводы, списания, пополнения. Ночью, пока клиенты спят, специальная программа должна пройти по каждой из этих операций, обновить остатки на счетах, отфильтровать подозрительные транзакции и сформировать итоговый отчёт для утра. Это и есть пакетная обработка (batch processing) — именно то, для чего COBOL создавался изначально и в чём он до сих пор силён на реальных мейнфреймах.
В отличие от сайта или приложения, где пользователь кликает и ждёт ответа за долю секунды, здесь программа работает часами напролёт, обрабатывая записи одну за другой без остановки. Поэтому код пишут особенно аккуратно: одна ошибка в цикле — и вместо двух миллионов операций обработается только половина или, того хуже, какая-то операция обработается дважды.
Как выглядит список транзакций
Сначала опишем, что вообще такое одна транзакция — то есть одна запись в нашем списке операций.
01 WS-TRANSAKCIYA.
05 WS-NOMER-SCHETA PIC 9(10).
05 WS-TIP-OPERACII PIC X(1).
88 POPOLNENIE VALUE "P".
88 SPISANIE VALUE "S".
05 WS-SUMMA PIC 9(7)V99.
01 WS-FLAG-KONEC-FAYLA PIC X(1) VALUE "N".
88 KONEC-FAYLA VALUE "Y".
01 WS-ITOGO-POPOLNENIY PIC 9(9)V99 VALUE ZERO.
01 WS-ITOGO-SPISANIY PIC 9(9)V99 VALUE ZERO.
01 WS-SCHETCHIK PIC 9(6) VALUE ZERO.Здесь появилась новая конструкция — уровень 88, который называется условное имя. Это одна из самых полезных придумок COBOL: вместо того чтобы потом в коде постоянно писать IF WS-TIP-OPERACII = "P", мы один раз объявляем, что «POPOLNENIE — это когда поле равно значению P», и дальше по всему коду можно писать просто IF POPOLNENIE — гораздо понятнее для человека, который читает программу спустя пять лет после её написания (а в банках такое случается сплошь и рядом, банковский COBOL-код нередко живёт десятилетиями). Точно так же WS-FLAG-KONEC-FAYLA с условным именем KONEC-FAYLA будет нашим флагом «список закончился» — стандартный приём в пакетной обработке.
Цикл обработки: PERFORM UNTIL
Теперь главное — сам цикл, который проходит по всем транзакциям. В COBOL для этого используют PERFORM ... UNTIL, что переводится примерно как «выполняй, пока не выполнится условие».
PERFORM UNTIL KONEC-FAYLA
PERFORM CHITAT-SLEDUYUSHCHUYU-TRANSAKCIYU
IF NOT KONEC-FAYLA
EVALUATE TRUE
WHEN POPOLNENIE
ADD WS-SUMMA TO WS-ITOGO-POPOLNENIY
WHEN SPISANIE
ADD WS-SUMMA TO WS-ITOGO-SPISANIY
WHEN OTHER
DISPLAY "Неизвестный тип операции по счёту "
WS-NOMER-SCHETA
END-EVALUATE
ADD 1 TO WS-SCHETCHIK
END-IF
END-PERFORM.
DISPLAY "Обработано транзакций: " WS-SCHETCHIK.
DISPLAY "Итого пополнений: " WS-ITOGO-POPOLNENIY.
DISPLAY "Итого списаний: " WS-ITOGO-SPISANIY.Разберём по шагам. PERFORM UNTIL KONEC-FAYLA — это заголовок цикла: «повторяй тело цикла, пока условное имя KONEC-FAYLA не станет истинным». Внутри цикла первым делом вызывается PERFORM CHITAT-SLEDUYUSHCHUYU-TRANSAKCIYU — это вызов отдельного именованного блока кода (представь его как вызов функции), который читает следующую транзакцию из списка и, если транзакций больше нет, устанавливает флаг WS-FLAG-KONEC-FAYLA в значение Y. Дальше идёт проверка IF NOT KONEC-FAYLA — она нужна, чтобы не пытаться обработать «пустую» транзакцию, если чтение как раз обнаружило конец списка на этом шаге.
Внутри — уже знакомая нам конструкция EVALUATE TRUE с условными именами POPOLNENIE и SPISANIE: для пополнений сумма прибавляется к общему итогу пополнений через ADD WS-SUMMA TO WS-ITOGO-POPOLNENIY, для списаний — к итогу списаний. Оператор ADD ... TO ... — это простое накопительное сложение, читается буквально «прибавь сумму к итогу». И снова ветка WHEN OTHER ловит операции с неожиданным типом — на реальном мейнфрейме в батче из двух миллионов записей рано или поздно попадётся кривая запись, и программа обязана на неё отреагировать, а не упасть или молча пропустить.
Практический пример: небольшой пакет вручную
Чтобы окончательно закрепить логику, разберём тот же принцип на маленьком «игрушечном» пакете из трёх транзакций — так же, как это работало бы с настоящим файлом, только данные мы зададим прямо в коде.
01 WS-INDEKS PIC 9(1) VALUE 1.
01 WS-VSEGO-ZAPISEY PIC 9(1) VALUE 3.
01 WS-TIPY-MASSIV.
05 WS-TIP OCCURS 3 TIMES PIC X(1) VALUE "P".
01 WS-SUMMY-MASSIV.
05 WS-SUMMA-EL OCCURS 3 TIMES PIC 9(5)V99.
MOVE "P" TO WS-TIP(1). MOVE 5000.00 TO WS-SUMMA-EL(1).
MOVE "S" TO WS-TIP(2). MOVE 1200.50 TO WS-SUMMA-EL(2).
MOVE "P" TO WS-TIP(3). MOVE 3300.00 TO WS-SUMMA-EL(3).
PERFORM VARYING WS-INDEKS FROM 1 BY 1
UNTIL WS-INDEKS > WS-VSEGO-ZAPISEY
IF WS-TIP(WS-INDEKS) = "P"
DISPLAY "Пополнение №" WS-INDEKS ": " WS-SUMMA-EL(WS-INDEKS)
ELSE
DISPLAY "Списание №" WS-INDEKS ": " WS-SUMMA-EL(WS-INDEKS)
END-IF
END-PERFORM.Здесь мы увидели ещё один вид цикла — PERFORM VARYING ... FROM ... BY ... UNTIL, это классический счётный цикл (аналог for в других языках): переменная WS-INDEKS начинается с 1, каждый шаг увеличивается на 1, и цикл идёт, пока индекс не превысит количество записей. Конструкция OCCURS 3 TIMES в описании поля превращает его в массив из трёх элементов — так COBOL хранит повторяющиеся однотипные данные, а обращение к конкретному элементу идёт через скобки, например WS-TIP(2) — второй элемент массива.
Вывод:
Пополнение №1: 05000.00
Списание №2: 01200.50
Пополнение №3: 03300.00Частые ошибки
Самая опасная ошибка в пакетной обработке — забыть проверить флаг конца списка сразу после чтения очередной записи, до того как эта запись пойдёт в обработку. Если пропустить проверку IF NOT KONEC-FAYLA, последняя «пустая» итерация может попасть в расчёт как настоящая транзакция с мусорными данными — и итоговая сумма в отчёте окажется неверной. Вторая частая ошибка — не сбросить накопительные поля в ноль перед началом обработки (у нас это сделано через VALUE ZERO в описании), иначе, если программа перезапустится или итоги пересчитываются повторно, к старым суммам прибавятся новые, и получится задвоение. Третья ошибка, характерная именно для новичков в пакетной обработке, — не предусмотреть ветку WHEN OTHER для непредвиденных типов операций: на живых данных всегда найдётся запись с опечаткой или битым полем, и без защитной ветки такая запись либо тихо потеряется, либо уронит всю программу посреди обработки миллионов записей.
Итоги
- Пакетная обработка — это цикл, который проходит по всем записям файла без участия человека, поэтому код должен быть особенно надёжным.
- Условные имена (уровень
88) делают код читаемым: вместо сравнений с «магическими» символами используются осмысленные имена вродеPOPOLNENIEилиKONEC-FAYLA. PERFORM UNTIL— цикл «пока условие не выполнено», аPERFORM VARYING— классический счётный цикл с индексом; оба активно используются в реальных программах.- Всегда проверяй флаг конца данных сразу после чтения и обнуляй накопительные поля перед стартом — это две самые частые причины неверных итогов в банковских отчётах.