Translate

terça-feira, 13 de agosto de 2019

SORT — O Pelotão que Colocava o Caos em Ordem

Bellacosa Mainframe e a classificacao de datasets via sort

☕ Um Café no Bellacosa Mainframe

SORT — O Pelotão que Colocava o Caos em Ordem

Como DFSORT, ICETOOL, arquivos temporários e algumas linhas de JCL reorganizam milhões de registros enquanto o recruta tenta sobreviver à sua primeira madrugada no CPD

“No batch, ninguém abandona um registro para trás.”

A sala de operações estava quase vazia.

Eram duas e quarenta e sete da manhã quando o recruta recebeu sua primeira missão de verdade. Não era um programa COBOL elegante, não havia tela CICS piscando nem banco de dados esperando um COMMIT.

Havia apenas um arquivo.

Um arquivo enorme.

Milhões de registros de transações, clientes, datas, agências e valores haviam chegado completamente fora de ordem. Alguns registros estavam duplicados. Outros deveriam ser eliminados. Certas colunas precisavam mudar de posição. O departamento financeiro queria um relatório por agência, em ordem de data e valor.

E queria antes das seis da manhã.

O veterano olhou para o recruta como o sargento Elias observando um soldado recém-chegado à selva de Platoon.

— Você sabe usar o SORT?

O jovem respirou fundo.

— Sei colocar em ordem crescente.

O veterano apagou o cigarro imaginário no cinzeiro do CPD.

— Então você ainda não sabe usar o SORT.


1. O que é o SORT no mainframe?

No universo IBM z/OS, quando alguém diz simplesmente SORT, normalmente está falando de uma família de operações capazes de:

  • classificar registros;

  • copiar arquivos;

  • mesclar arquivos previamente ordenados;

  • selecionar ou eliminar registros;

  • remover duplicidades;

  • somar campos numéricos;

  • reformatar registros;

  • criar vários arquivos de saída;

  • gerar relatórios;

  • contar registros;

  • comparar arquivos;

  • cruzar dois datasets por meio de chaves;

  • inserir cabeçalhos e rodapés;

  • converter formatos;

  • criar sequências;

  • procurar e substituir conteúdo.

Na plataforma IBM, a implementação mais conhecida é o DFSORT, produto de alto desempenho destinado a classificação, cópia, mesclagem, análise e geração de relatórios em z/OS. Apesar de seu nome histórico, ele está muito longe de ser apenas um classificador. (IBM)

Dependendo da instalação, também pode existir outro produto compatível, como o Syncsort/MFX. A sintaxe básica costuma ser semelhante, mas opções avançadas, mensagens e comportamentos podem variar.

Por isso, uma das primeiras regras do recruta é:

Antes de copiar um SORT encontrado na internet, descubra qual produto está instalado em seu ambiente.


2. A origem: antes do silício, já existia o problema da ordem

A necessidade de ordenar informações é muito anterior ao computador.

Bibliotecas classificavam livros. Bancos organizavam fichas de clientes. Governos agrupavam registros populacionais. Empresas separavam cartões perfurados por código, região, conta ou data.

Nas antigas instalações de processamento de dados, máquinas classificadoras eletromecânicas liam cartões perfurados e os distribuíam fisicamente em compartimentos. Para ordenar por vários campos, os cartões podiam passar diversas vezes pela máquina.

Primeiro uma coluna.

Depois outra.

Depois outra.

Era uma coreografia mecânica de papel, engrenagens e eletricidade.

Quando os computadores eletrônicos assumiram o processamento comercial, aquela necessidade permaneceu. O suporte mudou — cartões, fitas magnéticas, discos, datasets —, mas a missão continuou a mesma:

Receber dados desordenados e entregá-los na sequência necessária ao processamento seguinte.

Em ambientes batch, a ordenação tornou-se fundamental porque muitos programas foram projetados para trabalhar com arquivos em sequência de chave.

Um programa COBOL poderia, por exemplo:

  1. ler um arquivo de clientes em ordem de número de conta;

  2. ler um arquivo de movimentos na mesma ordem;

  3. comparar as chaves;

  4. aplicar os movimentos ao cliente correspondente;

  5. produzir um novo arquivo mestre.

Sem a classificação prévia, esse casamento sequencial seria caótico.

O SORT tornou-se, portanto, uma espécie de pelotão de logística do mainframe. Ele não aparece na linha de frente das aplicações, mas prepara o terreno para que todo o restante funcione.


3. Anatomia de uma missão SORT

Um passo básico de DFSORT em JCL costuma ter quatro elementos:

//ORDENA   EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=VAGNER.ARQUIVO.ENTRADA,DISP=SHR
//SORTOUT  DD DSN=VAGNER.ARQUIVO.SAIDA,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(10,5),RLSE),
//            DCB=(RECFM=FB,LRECL=100,BLKSIZE=0)
//SYSIN    DD *
  SORT FIELDS=(1,10,CH,A)
/*

O papel de cada DD

EXEC PGM=SORT

Executa o utilitário de classificação definido pela instalação.

Em muitos ambientes também aparece:

//ORDENA EXEC PGM=ICEMAN

ICEMAN é um nome tradicionalmente associado ao programa do DFSORT, mas PGM=SORT é mais legível e costuma ser preferido em JCL de aplicação.

SYSOUT

Recebe mensagens produzidas pelo utilitário:

//SYSOUT DD SYSOUT=*

É aqui que o recruta descobrirá:

  • quantos registros foram lidos;

  • quantos foram gravados;

  • quais opções foram utilizadas;

  • se houve alocação dinâmica;

  • qual foi o return code;

  • qual mensagem explica o desastre.

Nunca despreze o SYSOUT.

No campo de batalha do batch, ele é o relatório da patrulha.

SORTIN

É o dataset de entrada:

//SORTIN DD DSN=VAGNER.ARQUIVO.ENTRADA,DISP=SHR

SORTOUT

É o arquivo principal de saída:

//SORTOUT DD DSN=VAGNER.ARQUIVO.SAIDA,...

SYSIN

Contém as instruções de controle:

//SYSIN DD *
  SORT FIELDS=(1,10,CH,A)
/*

É no SYSIN que a missão é descrita.


4. Decifrando SORT FIELDS

Observe:

SORT FIELDS=(1,10,CH,A)

A estrutura é:

posição, tamanho, formato, ordem

Portanto:

1,10,CH,A

significa:

  • começar na posição 1;

  • usar 10 bytes;

  • interpretar como caracteres;

  • ordenar de forma ascendente.

Ordem ascendente e descendente

A = Ascending
D = Descending

Exemplo:

  SORT FIELDS=(1,10,CH,A)

Ordena nomes da posição 1 à 10 em ordem crescente.

  SORT FIELDS=(1,10,CH,D)

Ordena em ordem decrescente.


5. Ordenação com várias chaves

Imagine este layout:

PosiçãoTamanhoCampo
14Agência
58Data AAAAMMDD
1310Número da conta
239Valor
321Tipo

Queremos ordenar:

  1. por agência crescente;

  2. dentro da agência, por data crescente;

  3. dentro da data, por valor decrescente.

//SYSIN DD *
  SORT FIELDS=(1,4,CH,A,
               5,8,CH,A,
               23,9,ZD,D)
/*

O DFSORT aplica as chaves da esquerda para a direita.

É como organizar um pelotão:

  1. primeiro por companhia;

  2. depois por esquadrão;

  3. finalmente por graduação.


6. Os principais formatos de dados

O SORT precisa saber como interpretar os bytes.

A sequência hexadecimal que representa o número 123 em display não é a mesma representação de um campo binário ou packed decimal.

Entre os formatos suportados estão diversos tipos de caracteres, binários e decimais. O formato CH, por exemplo, trata cada caractere segundo seu código EBCDIC. (IBM)

Formatos comuns

CH — Character

1,20,CH,A

Usado para campos alfanuméricos em EBCDIC.

ZD — Zoned Decimal

Muito comum em arquivos originados por COBOL:

05 WS-VALOR PIC 9(9).

No SORT:

23,9,ZD,D

PD — Packed Decimal

Corresponde normalmente a campos COBOL COMP-3.

05 WS-VALOR PIC S9(9)V99 COMP-3.

O tamanho físico não é 11 bytes. Para 11 dígitos, o campo packed ocupa:

(11 + 1) / 2 = 6 bytes

Exemplo:

23,6,PD,D

BI — Binary

Trata o campo como binário sem sinal.

FI — Fixed-point signed

Usado em certos campos binários com sinal.

UFF e SFF

Úteis para números em formato livre, com espaços ou sinais em diferentes posições.


7. A armadilha do recruta: ordenar números como caracteres

Considere:

1
2
10
20
100

Se os valores ocuparem campos de tamanho variável ou não estiverem preenchidos corretamente, uma classificação alfanumérica pode produzir algo semelhante a:

1
10
100
2
20

Isso ocorre porque a comparação está sendo feita byte a byte, como texto.

Para números, use o formato correspondente à representação física real:

  SORT FIELDS=(1,5,ZD,A)

Mas atenção:

Não escolha ZD, PD ou BI pelo significado lógico do campo. Escolha pela forma como ele está fisicamente gravado.

Um valor pode ser conceitualmente numérico e, ainda assim, estar armazenado como caracteres.


8. Como descobrir as posições dos campos

Suponha o copybook:

01 REGISTRO-CLIENTE.
   05 CLI-AGENCIA      PIC X(04).
   05 CLI-CONTA        PIC X(10).
   05 CLI-NOME         PIC X(30).
   05 CLI-SALDO        PIC S9(09)V99 COMP-3.
   05 CLI-STATUS       PIC X(01).

Calculando as posições:

CampoPosiçãoTamanho físico
CLI-AGENCIA14
CLI-CONTA510
CLI-NOME1530
CLI-SALDO456
CLI-STATUS511

Para ordenar por agência, saldo decrescente e conta:

  SORT FIELDS=(1,4,CH,A,
               45,6,PD,D,
               5,10,CH,A)

Regra de ouro

Antes de escrever o SORT:

  1. obtenha o copybook;

  2. confirme o RECFM;

  3. confirme o LRECL;

  4. calcule a posição física;

  5. verifique campos COMP, COMP-3 e redefinições;

  6. confirme se existe RDW em registros variáveis;

  7. examine uma amostra em hexadecimal.

O programador que não olha os dados em hexadecimal está entrando na selva sem mapa.


9. Arquivos FB e VB: a história dos quatro bytes invisíveis

Arquivo FB

Em um dataset RECFM=FB, todos os registros têm tamanho fixo.

Se o LRECL é 100, o primeiro campo começa normalmente na posição 1 do registro lógico.

  SORT FIELDS=(1,10,CH,A)

Arquivo VB

Em um dataset RECFM=VB, cada registro físico começa com um Record Descriptor Word, o famoso RDW.

O RDW ocupa quatro bytes.

Por isso, em muitas instruções DFSORT para arquivos variáveis, os dados começam na posição 5.

Se o campo lógico está no início do registro de aplicação, a chave poderá ser:

  SORT FIELDS=(5,10,CH,A)

Este é um dos erros clássicos de iniciantes.

O recruta vê o copybook dizendo que o campo começa na posição 1 e escreve:

1,10,CH,A

Mas, no contexto do SORT para VB, acaba incluindo os bytes do RDW.

O resultado pode ser:

  • classificação absurda;

  • erro de campo além do registro;

  • comportamento variável;

  • longas horas procurando um problema que estava quatro bytes antes.


10. SORT FIELDS=COPY: quando não queremos classificar

DFSORT também pode apenas copiar registros:

//COPIA    EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=VAGNER.CLIENTES,DISP=SHR
//SORTOUT  DD DSN=VAGNER.CLIENTES.COPIA,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(5,2),RLSE),
//            UNIT=SYSDA,
//            DCB=*.SORTIN
//SYSIN    DD *
  SORT FIELDS=COPY
/*

Também pode ser usado:

OPTION COPY

A cópia parece banal, mas é extremamente poderosa quando combinada com:

  • INCLUDE;

  • OMIT;

  • INREC;

  • OUTREC;

  • OUTFIL;

  • FINDREP;

  • IFTHEN.

É comum usar DFSORT apenas como um transformador de arquivos, sem alterar a ordem.


11. Selecionando registros com INCLUDE

Imagine que o status esteja na posição 51.

Queremos somente clientes ativos:

  INCLUDE COND=(51,1,CH,EQ,C'A')
  SORT FIELDS=COPY

Operadores de comparação

EQ = Equal
NE = Not equal
GT = Greater than
GE = Greater than or equal
LT = Less than
LE = Less than or equal

Exemplo com mais de uma condição

Selecionar:

  • status ativo;

  • saldo maior que zero.

  INCLUDE COND=(51,1,CH,EQ,C'A',AND,
                45,6,PD,GT,+0)
  SORT FIELDS=(1,4,CH,A)

Uso de OR

  INCLUDE COND=(51,1,CH,EQ,C'A',OR,
                51,1,CH,EQ,C'P')
  SORT FIELDS=COPY

Aceita status A ou P.


12. Eliminando registros com OMIT

Se a missão for remover cancelados:

  OMIT COND=(51,1,CH,EQ,C'C')
  SORT FIELDS=COPY

Nunca use INCLUDE e OMIT simultaneamente no mesmo nível de controle. Escolha a lógica mais clara.

Como regra de manutenção:

  • use INCLUDE quando poucos registros devem passar;

  • use OMIT quando poucos registros devem ser descartados.


13. Constantes no SORT

Caracteres

C'ATIVO'

Hexadecimal

X'F1F2F3'

Decimal positivo

+100

Decimal negativo

-50

Exemplo:

  INCLUDE COND=(1,3,CH,EQ,C'BRA')

14. Removendo registros duplicados

Considere um arquivo ordenado por conta.

Queremos manter apenas um registro para cada conta:

  SORT FIELDS=(5,10,CH,A)
  SUM FIELDS=NONE

SUM FIELDS=NONE elimina registros cujas chaves de classificação sejam duplicadas.

Mas há uma pergunta perigosa:

Qual registro duplicado será mantido?

Isso depende das opções de estabilidade e da estratégia utilizada.

Para preservar a ordem relativa original de registros com a mesma chave, pode-se usar:

  OPTION EQUALS
  SORT FIELDS=(5,10,CH,A)
  SUM FIELDS=NONE

EQUALS ajuda a preservar a ordem de entrada entre registros com chaves iguais.

Mesmo assim, não trate a seleção de “primeiro” ou “último” duplicado como acidente. Quando a regra de negócio depende disso, torne-a explícita adicionando uma chave complementar, como data ou timestamp.

Exemplo: manter o movimento mais recente.

  SORT FIELDS=(5,10,CH,A,
               60,14,CH,D)
  SUM FIELDS=NONE

Aqui:

  • conta crescente;

  • timestamp decrescente;

  • o registro mais recente aparece primeiro dentro da conta;

  • os demais duplicados são eliminados.


15. Somando valores com SUM

Suponha:

  • agência: posição 1, tamanho 4;

  • valor packed: posição 45, tamanho 6.

Queremos consolidar o valor total por agência:

  SORT FIELDS=(1,4,CH,A)
  SUM FIELDS=(45,6,PD)

Todos os registros com a mesma chave serão consolidados, e o valor será somado.

É essencial verificar:

  • capacidade do campo de saída;

  • sinal;

  • casas decimais implícitas;

  • possível overflow;

  • existência de outros campos não pertencentes à chave.

Campos não somados poderão vir de um dos registros do grupo. Portanto, não presuma que todo o conteúdo do registro consolidado terá significado lógico.


16. INREC: preparando o soldado antes da batalha

INREC reformata cada registro antes da classificação principal.

O fluxo conceitual é:

Entrada
  ↓
INCLUDE/OMIT
  ↓
INREC
  ↓
SORT ou MERGE
  ↓
SUM
  ↓
OUTREC
  ↓
OUTFIL
  ↓
Saída

A documentação IBM explica que INREC é processado antes de SORT, SUM e OUTREC, mas depois das condições INCLUDE ou OMIT. (IBM)

Exemplo

Arquivo original:

CampoPosiçãoTamanho
Conta110
Nome1130
Agência414
Saldo456

Queremos criar internamente:

Agência + Conta + Saldo
  INREC BUILD=(41,4,
               1,10,
               45,6)
  SORT FIELDS=(1,4,CH,A,
               5,10,CH,A)

Depois do INREC, as posições mudaram.

Agora:

  • agência está em 1;

  • conta está em 5;

  • saldo está em 15.

Uma das maiores fontes de erro é continuar usando no SORT FIELDS as posições do registro original após um INREC.


17. OUTREC: reorganizando depois da classificação

OUTREC reformata os registros depois da operação de ordenação, cópia ou mesclagem. (IBM)

Exemplo:

  SORT FIELDS=(1,4,CH,A)
  OUTREC BUILD=(1,4,
                C'|',
                5,10,
                C'|',
                15,30)

Saída:

0001|1234567890|JOSE DA SILVA
0001|2234567890|MARIA DE SOUZA

Inserindo espaços

  OUTREC BUILD=(10X,1,20,5X,21,10)

A documentação IBM permite inserir áreas em branco com notações como 20X ou 10X. (IBM)

Inserindo texto literal

  OUTREC BUILD=(C'AGENCIA=',1,4,
                C' CONTA=',5,10)

18. OVERLAY: cirurgia em posições específicas

BUILD constrói um novo registro.

OVERLAY altera partes específicas do registro, preservando o restante.

Exemplo: colocar PROCESSADO na posição 80:

  SORT FIELDS=COPY
  OUTREC OVERLAY=(80:C'PROCESSADO')

Exemplo: substituir um status:

  OUTREC IFTHEN=(WHEN=(51,1,CH,EQ,C'A'),
                 OVERLAY=(51:C'X'))

19. IFTHEN: regras condicionais

É possível aplicar transformações diferentes dependendo do conteúdo.

  SORT FIELDS=COPY
  OUTREC IFTHEN=(WHEN=(51,1,CH,EQ,C'A'),
                 OVERLAY=(60:C'CLIENTE ATIVO')),
         IFTHEN=(WHEN=(51,1,CH,EQ,C'I'),
                 OVERLAY=(60:C'CLIENTE INATIVO'))

Cláusulas IFTHEN podem selecionar grupos de registros e aplicar operações como BUILD, OVERLAY ou FINDREP. (IBM)

Outras formas úteis incluem:

WHEN=INIT
WHEN=GROUP
WHEN=ANY
WHEN=NONE

Exemplo com inicialização

  OUTREC IFTHEN=(WHEN=INIT,
                 OVERLAY=(80:C'NAO CLASSIFICADO')),
         IFTHEN=(WHEN=(51,1,CH,EQ,C'A'),
                 OVERLAY=(80:C'ATIVO'))

Primeiro todos recebem NAO CLASSIFICADO. Depois os ativos recebem ATIVO.


20. FINDREP: localizar e substituir

Imagine registros contendo:

SAO PAULO

Queremos trocar por:

SP
  SORT FIELDS=COPY
  OUTREC FINDREP=(IN=C'SAO PAULO',
                  OUT=C'SP')

Também pode substituir várias expressões:

  OUTREC FINDREP=(INOUT=(C'SAO PAULO',C'SP',
                         C'RIO DE JANEIRO',C'RJ',
                         C'MINAS GERAIS',C'MG'))

Use com cuidado em arquivos de layout fixo. Uma substituição que muda o tamanho do conteúdo pode deslocar campos ou alterar o LRECL.


21. OUTFIL: vários destinos em uma única passagem

OUTFIL permite produzir um ou vários arquivos a partir de uma única leitura e de uma única operação principal. A IBM documenta que múltiplas instruções OUTFIL podem gerar diferentes saídas com filtros e reformatações próprias. (IBM)

Imagine um arquivo com status na posição 51.

Queremos:

  • ativos em ATIVOS;

  • inativos em INATIVOS;

  • cancelados em CANCELA.

//ATIVOS   DD DSN=VAGNER.CLIENTES.ATIVOS,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(5,2),RLSE),
//            UNIT=SYSDA
//INATIVOS DD DSN=VAGNER.CLIENTES.INATIVOS,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(5,2),RLSE),
//            UNIT=SYSDA
//CANCELA  DD DSN=VAGNER.CLIENTES.CANCELADOS,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(5,2),RLSE),
//            UNIT=SYSDA
//SYSIN    DD *
  SORT FIELDS=(1,4,CH,A)

  OUTFIL FNAMES=ATIVOS,
         INCLUDE=(51,1,CH,EQ,C'A')

  OUTFIL FNAMES=INATIVOS,
         INCLUDE=(51,1,CH,EQ,C'I')

  OUTFIL FNAMES=CANCELA,
         INCLUDE=(51,1,CH,EQ,C'C')
/*

Isso evita:

  • três leituras do arquivo;

  • três passos distintos;

  • três classificações;

  • consumo desnecessário de CPU e I/O.


22. SAVE: recolhendo quem ficou para trás

Depois de criar saídas específicas, podemos usar SAVE para capturar tudo que não entrou nas anteriores:

  OUTFIL FNAMES=ATIVOS,
         INCLUDE=(51,1,CH,EQ,C'A')

  OUTFIL FNAMES=INATIVOS,
         INCLUDE=(51,1,CH,EQ,C'I')

  OUTFIL FNAMES=OUTROS,
         SAVE

A saída OUTROS funciona como uma rede de segurança.

Em produção, ela é excelente para detectar códigos inesperados.


23. Cabeçalhos, rodapés e relatórios

O DFSORT também pode produzir relatórios.

  SORT FIELDS=(1,4,CH,A)

  OUTFIL FNAMES=RELATOR,
         HEADER1=(C'RELATORIO DE CLIENTES'),
         HEADER2=(C'AGENCIA   CONTA       NOME'),
         TRAILER1=(C'FIM DO RELATORIO')

É possível usar recursos como:

DATE
TIME
PAGE
COUNT
TOT
MIN
MAX
AVG

Exemplo conceitual:

  OUTFIL FNAMES=RELATOR,
         HEADER1=(C'RELATORIO DIARIO - ',DATE),
         TRAILER1=(C'TOTAL DE REGISTROS: ',COUNT)

A sintaxe exata pode variar conforme o tipo de relatório e a edição desejada.


24. MERGE: unindo pelotões já organizados

Se dois ou mais arquivos já estiverem classificados pela mesma chave, podemos mesclá-los sem realizar uma nova classificação completa.

//JUNTA    EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN01 DD DSN=VAGNER.MOVIMENTO.DIA1,DISP=SHR
//SORTIN02 DD DSN=VAGNER.MOVIMENTO.DIA2,DISP=SHR
//SORTOUT  DD DSN=VAGNER.MOVIMENTO.CONSOLIDADO,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(20,10),RLSE),
//            UNIT=SYSDA
//SYSIN    DD *
  MERGE FIELDS=(1,10,CH,A)
/*

Os arquivos de entrada precisam estar previamente ordenados pela chave especificada.

Não use MERGE esperando que ele conserte arquivos desordenados.


25. JOINKEYS: quando dois arquivos precisam se encontrar

JOINKEYS permite combinar registros de dois datasets por uma ou mais chaves.

É semelhante ao conceito de JOIN em SQL.

A IBM documenta suporte a combinações equivalentes a:

  • inner join;

  • left outer join;

  • right outer join;

  • full outer join;

  • registros pareados;

  • registros não pareados.

Os arquivos podem ter formatos e comprimentos diferentes. (IBM)

Exemplo: clientes e movimentos

Arquivo F1 — clientes

PosiçãoCampo
1–10Conta
11–40Nome

Arquivo F2 — movimentos

PosiçãoCampo
1–10Conta
11–18Data
19–27Valor
//CRUZA    EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTJNF1 DD DSN=VAGNER.CLIENTES,DISP=SHR
//SORTJNF2 DD DSN=VAGNER.MOVIMENTOS,DISP=SHR
//SORTOUT  DD DSN=VAGNER.CLIENTE.MOVIMENTO,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(10,5),RLSE),
//            UNIT=SYSDA
//SYSIN    DD *
  JOINKEYS FILE=F1,FIELDS=(1,10,A)
  JOINKEYS FILE=F2,FIELDS=(1,10,A)

  REFORMAT FIELDS=(F1:1,40,
                   F2:11,17)

  SORT FIELDS=COPY
/*

O resultado contém:

  • dados do cliente;

  • data e valor do movimento correspondente.

Apenas registros correspondentes

Esse é o comportamento básico de um inner join.

Registros sem correspondência

Para incluir não pareados:

  JOIN UNPAIRED,F1,F2

Para identificar a origem, pode-se usar um indicador na reformatação:

  REFORMAT FIELDS=(F1:1,40,
                   F2:11,17,
                   ?)

O caractere indicador ajuda a distinguir:

  • registros encontrados nos dois lados;

  • apenas no F1;

  • apenas no F2.


26. Arquivos temporários no JCL

Um dataset temporário existe somente durante a execução do job.

Seu nome começa com &&.

Exemplo:

//TEMP1 DD DSN=&&CLIENTES,
//         DISP=(NEW,PASS),
//         UNIT=SYSDA,
//         SPACE=(CYL,(10,5)),
//         DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)

&&CLIENTES

É um nome temporário.

O sistema cria um nome interno para o dataset, que será removido ao final do job.

DISP=(NEW,PASS)

  • NEW: o dataset será criado;

  • PASS: se o passo terminar adequadamente, o dataset será passado para um passo seguinte.

Exemplo completo com dois passos

No primeiro passo, selecionamos clientes ativos.

No segundo, classificamos por agência.

//FILTRA   EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=VAGNER.CLIENTES.MESTRE,DISP=SHR
//SORTOUT  DD DSN=&&ATIVOS,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(CYL,(10,5)),
//            DCB=(RECFM=FB,LRECL=100,BLKSIZE=0)
//SYSIN    DD *
  INCLUDE COND=(51,1,CH,EQ,C'A')
  SORT FIELDS=COPY
/*
//ORDENA   EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=&&ATIVOS,DISP=(OLD,DELETE)
//SORTOUT  DD DSN=VAGNER.CLIENTES.ATIVOS.ORD,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(10,5),RLSE),
//            DCB=(RECFM=FB,LRECL=100,BLKSIZE=0)
//SYSIN    DD *
  SORT FIELDS=(1,4,CH,A,
               15,30,CH,A)
/*

No segundo passo:

DISP=(OLD,DELETE)

indica que o arquivo será utilizado e removido depois.

Mas o veterano perguntaria:

— Por que dois passos?

A mesma operação poderia ser feita em um:

  INCLUDE COND=(51,1,CH,EQ,C'A')
  SORT FIELDS=(1,4,CH,A,
               15,30,CH,A)

Arquivos temporários são úteis, mas não devem substituir uma boa análise.

Use-os quando:

  • o resultado intermediário será utilizado por vários passos;

  • a separação facilita recuperação e restart;

  • existe necessidade operacional de inspeção;

  • diferentes utilitários precisam consumir o resultado;

  • o processo requer checkpoints;

  • a transformação não pode ser feita em uma única operação.


27. SORTWKnn: a trilha de suprimentos

Durante uma classificação, o DFSORT pode precisar de espaço de trabalho.

Quando os dados não cabem integralmente nas estruturas internas disponíveis, partes intermediárias são gravadas em work datasets.

Esses arquivos costumam ter DD names como:

SORTWK01
SORTWK02
SORTWK03
...

Exemplo de alocação manual:

//SORTWK01 DD UNIT=SYSDA,SPACE=(CYL,(500,100))
//SORTWK02 DD UNIT=SYSDA,SPACE=(CYL,(500,100))
//SORTWK03 DD UNIT=SYSDA,SPACE=(CYL,(500,100))
//SORTWK04 DD UNIT=SYSDA,SPACE=(CYL,(500,100))

Alocação manual ou dinâmica?

Em instalações modernas, frequentemente é melhor permitir que o DFSORT faça a alocação dinâmica.

A IBM afirma que a alocação dinâmica pode ajustar automaticamente o espaço de trabalho às características reais da aplicação, enquanto uma alocação fixa em JCL não pode ser ajustada pelo DFSORT e pode desperdiçar espaço. (IBM)

Portanto, evite copiar cegamente dezenas de SORTWKnn de um JCL antigo.

Talvez aquelas linhas tenham sido necessárias vinte anos atrás.

Talvez hoje estejam apenas ocupando espaço, limitando a otimização ou confundindo o diagnóstico.

Quando especificar manualmente?

Pode fazer sentido quando:

  • a instalação exige volumes específicos;

  • existe política local de storage;

  • o SORT não consegue estimar corretamente a entrada;

  • a alocação dinâmica está desabilitada;

  • há necessidade de isolar workloads;

  • o job apresenta falhas recorrentes de espaço;

  • a equipe de storage ou performance recomendou explicitamente.

Não direcione automaticamente SORTWKnn para VIO. A IBM recomenda evitar VIO para datasets temporários de trabalho do DFSORT e considerar os mecanismos próprios de memória e Hipersorting. (IBM)


28. Como o SORT trabalha com grandes datasets

Imagine um arquivo de 500 milhões de registros.

O DFSORT não precisa necessariamente carregar tudo em uma única área convencional e então “apertar um botão”.

Conceitualmente, uma classificação externa pode envolver:

  1. leitura de blocos da entrada;

  2. criação de sequências ordenadas intermediárias;

  3. uso de memória e áreas de trabalho;

  4. gravação de runs intermediários;

  5. intercalação dessas sequências;

  6. produção da saída final.

As implementações modernas usam diversas otimizações internas. O DFSORT pode explorar recursos como:

  • armazenamento virtual;

  • objetos de memória de 64 bits;

  • data spaces;

  • Hiperspace;

  • buffers;

  • leitura e escrita em blocos;

  • alocação dinâmica de work files;

  • estimativa do tamanho da entrada.

A IBM documenta que o DFSORT pode selecionar modos que usam objetos de memória em armazenamento virtual de 64 bits, além de outras estratégias para otimizar a classificação. (IBM)

O programador de aplicação não deve tentar adivinhar todo o algoritmo interno.

Mas precisa fornecer informações corretas.


29. Como ajudar o SORT em arquivos gigantes

29.1 Informe corretamente os atributos

Confirme:

  • RECFM;

  • LRECL;

  • tamanho estimado;

  • quantidade de registros;

  • tipo de dispositivo;

  • compressão;

  • organização;

  • concatenações;

  • entrada em fita ou disco.

29.2 Filtre o mais cedo possível

Se apenas 5% dos registros interessam, use INCLUDE antes da classificação:

  INCLUDE COND=(51,1,CH,EQ,C'A')
  SORT FIELDS=(1,10,CH,A)

Não classifique 500 milhões de registros para depois descartar 475 milhões.

29.3 Reduza o tamanho do registro antes de ordenar

Se a entrada possui 2.000 bytes, mas a classificação precisa de apenas 40, use INREC para reduzir o registro temporário:

  INREC BUILD=(1,10,
               500,8,
               1500,22)
  SORT FIELDS=(1,10,CH,A)

Menos bytes significam potencialmente:

  • menos memória;

  • menos I/O;

  • menos espaço de trabalho;

  • menos tempo de transferência;

  • menos espaço de saída intermediária.

Mas lembre-se de reconstruir o formato necessário, caso a saída precise do registro original.

29.4 Evite passos desnecessários

Ruim:

Passo 1: copiar
Passo 2: filtrar
Passo 3: ordenar
Passo 4: separar saídas
Passo 5: gerar relatório

Talvez tudo possa ser feito em um passo com:

INCLUDE
SORT
OUTREC
OUTFIL

29.5 Use MERGE quando as entradas já estiverem ordenadas

Não reordene dois arquivos gigantes se ambos já estiverem na sequência correta.

29.6 Use OUTFIL para múltiplas saídas

Evite reler o mesmo arquivo várias vezes.

29.7 Verifique a chave

Ordenar por 200 bytes quando a chave real ocupa 10 bytes aumenta o trabalho sem necessidade.

29.8 Não use parâmetros misteriosos sem medir

Parâmetros de memória, alocação e performance dependem:

  • da versão;

  • do volume;

  • da instalação;

  • do WLM;

  • do storage;

  • das opções padronizadas;

  • da concorrência;

  • do horário.

O parâmetro que salvou um job em 2009 pode degradar outro em 2026.


30. Tipos de classificação

30.1 Ascendente

  SORT FIELDS=(1,10,CH,A)

30.2 Descendente

  SORT FIELDS=(1,10,CH,D)

30.3 Alfanumérica

  SORT FIELDS=(1,20,CH,A)

A ordem segue a representação e as regras de comparação aplicáveis, normalmente baseadas em EBCDIC para CH.

Não espere automaticamente a mesma ordem de um PC em ASCII.

30.4 Numérica display

  SORT FIELDS=(1,9,ZD,A)

30.5 Packed decimal

  SORT FIELDS=(1,6,PD,D)

30.6 Binária

  SORT FIELDS=(1,4,BI,A)

30.7 Múltiplas chaves

  SORT FIELDS=(1,4,CH,A,
               5,8,CH,D,
               13,6,PD,D)

30.8 Estável

  OPTION EQUALS

Mantém a ordem relativa dos registros com chaves iguais, dentro das regras da operação.

30.9 Não estável

  OPTION NOEQUALS

Pode permitir estratégias diferentes para registros com chaves iguais. Não dependa da ordem relativa nesse caso.

30.10 Com sequência alternativa

O SORT pode trabalhar com tabelas de tradução ou sequências de comparação específicas, conforme recursos e opções do ambiente.


31. Criando um número sequencial

É possível adicionar uma sequência aos registros:

  SORT FIELDS=(1,10,CH,A)
  OUTREC BUILD=(SEQNUM,8,ZD,
                1,100)

Saída:

00000001...
00000002...
00000003...

Também podemos usar reinicialização por grupo em construções mais avançadas com WHEN=GROUP.


32. Agrupando registros

Considere:

HCLIENTE001
DTRANSACAO1
DTRANSACAO2
T00000002
HCLIENTE002
DTRANSACAO1
T00000001

Temos:

  • H: header;

  • D: detail;

  • T: trailer.

Com recursos de agrupamento, é possível propagar dados do header para os detalhes, numerar grupos e tratar conjuntos relacionados.

Exemplo conceitual:

  INREC IFTHEN=(WHEN=GROUP,
                BEGIN=(1,1,CH,EQ,C'H'),
                PUSH=(81:ID=4))

Cada grupo recebe uma identificação.

Recursos de WHEN=GROUP são extremamente poderosos para arquivos hierárquicos, mas precisam ser testados com:

  • grupo sem trailer;

  • detalhe sem header;

  • dois headers consecutivos;

  • grupo vazio;

  • último grupo incompleto.


33. Preservando header e trailer com ICETOOL DATASORT

Às vezes o arquivo possui:

HEADER
DADOS
DADOS
DADOS
TRAILER

Queremos ordenar apenas os dados, mantendo o header no início e o trailer no final.

O operador DATASORT do ICETOOL foi criado justamente para classificar os registros de dados preservando registros de cabeçalho e rodapé. A IBM informa que esse operador usa EQUALS para manter a ordem relativa dos duplicados. (IBM)

Exemplo:

//TOOL     EXEC PGM=ICETOOL
//TOOLMSG  DD SYSOUT=*
//DFSMSG   DD SYSOUT=*
//IN       DD DSN=VAGNER.ARQUIVO.COMPLETO,DISP=SHR
//OUT      DD DSN=VAGNER.ARQUIVO.ORDENADO,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(10,5),RLSE)
//TOOLIN   DD *
  DATASORT FROM(IN) TO(OUT) HEADER TRAILER USING(CTL1)
/*
//CTL1CNTL DD *
  SORT FIELDS=(1,10,CH,A)
/*

34. ICETOOL: o canivete de campanha

ICETOOL é um utilitário multifuncional baseado nas capacidades do DFSORT e capaz de executar múltiplas operações em um único passo. (IBM)

O JCL básico:

//TOOL     EXEC PGM=ICETOOL
//TOOLMSG  DD SYSOUT=*
//DFSMSG   DD SYSOUT=*
//ENTRADA  DD DSN=VAGNER.ARQUIVO,DISP=SHR
//TOOLIN   DD *
  ...
/*

Operadores comuns

COPY

Copia dados.

SORT

Classifica dados.

COUNT

Conta registros.

SELECT

Seleciona registros por ocorrência de chave.

UNIQUE

Mostra chaves únicas ou conta ocorrências, conforme construção utilizada.

OCCUR

Produz estatísticas de ocorrência.

DISPLAY

Gera relatórios formatados.

VERIFY

Verifica campos numéricos.

STATS

Produz estatísticas de campos numéricos.

RANGE

Seleciona valores dentro ou fora de intervalos.

SUBSET

Seleciona partes do arquivo.

DATASORT

Ordena dados preservando headers e trailers.


35. Contando registros com ICETOOL

//TOOL     EXEC PGM=ICETOOL
//TOOLMSG  DD SYSOUT=*
//DFSMSG   DD SYSOUT=*
//ENTRADA  DD DSN=VAGNER.CLIENTES,DISP=SHR
//TOOLIN   DD *
  COUNT FROM(ENTRADA)
/*

Também podemos testar condições.

Exemplo conceitual:

  COUNT FROM(ENTRADA) EMPTY RC8

Se o arquivo estiver vazio, retorna RC 8.

Isso permite controlar passos posteriores com JCL:

//PROXIMO IF (TOOL.RC = 0) THEN
...
//        ENDIF

Os return codes do ICETOOL podem representar tanto erros quanto condições programadas por operadores como COUNT. (IBM)


36. Selecionando duplicados com ICETOOL

Suponha que a conta esteja em 1–10.

Para selecionar chaves que aparecem mais de uma vez:

//TOOL     EXEC PGM=ICETOOL
//TOOLMSG  DD SYSOUT=*
//DFSMSG   DD SYSOUT=*
//IN       DD DSN=VAGNER.CONTAS,DISP=SHR
//DUP      DD DSN=VAGNER.CONTAS.DUPLICADAS,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(5,2),RLSE)
//TOOLIN   DD *
  SELECT FROM(IN) TO(DUP) ON(1,10,CH) ALLDUPS
/*

Outras possibilidades podem selecionar:

  • primeira ocorrência;

  • última ocorrência;

  • valores que aparecem exatamente n vezes;

  • valores acima ou abaixo de determinada frequência;

  • registros únicos.

O operador SELECT utiliza classificação e pode preservar a ordem dos duplicados por meio de EQUALS. (IBM)


37. Validando campos numéricos

Antes de somar um campo packed ou zoned, convém verificar se os dados são válidos.

Um byte inválido pode derrubar a operação ou produzir resultado incorreto.

Com ICETOOL, pode-se utilizar VERIFY.

Exemplo conceitual:

  VERIFY FROM(IN) ON(45,6,PD)

Essa é uma excelente missão preventiva antes de processar arquivos vindos de:

  • plataformas distribuídas;

  • conversões ASCII/EBCDIC;

  • uploads manuais;

  • arquivos construídos por scripts;

  • parceiros externos;

  • layouts alterados sem controle.


38. Ordenando datas

Data no formato AAAAMMDD

Se o campo possui:

20260730
20251201
20270115

pode ser ordenado como CH ou ZD, desde que esteja consistentemente preenchido:

  SORT FIELDS=(1,8,CH,A)

O formato AAAAMMDD foi praticamente feito para ordenação lexicográfica.

Data DDMMYYYY

Considere:

30122025
01012026
15072024

Ordenar diretamente como texto não produz ordem cronológica.

Podemos ordenar por:

  1. ano;

  2. mês;

  3. dia.

  SORT FIELDS=(5,4,CH,A,
               3,2,CH,A,
               1,2,CH,A)

Ou criar uma chave temporária com INREC.

  INREC BUILD=(5,4,
               3,2,
               1,2,
               1,80)
  SORT FIELDS=(1,8,CH,A)
  OUTREC BUILD=(9,80)

A chave AAAAMMDD é acrescentada antes do registro, utilizada no SORT e removida na saída.

Esse truque é um clássico.


39. Ordenação case-insensitive

Em EBCDIC, letras maiúsculas e minúsculas possuem valores diferentes.

Uma lista com:

ANA
Ana
ana
BRUNO
Bruno

pode não ficar na ordem humana esperada.

Uma abordagem é criar uma chave temporária convertida para maiúsculas, ordenar por ela e depois remover a chave.

Exemplo conceitual:

  INREC BUILD=(1,30,TRAN=LTOU,
               1,100)
  SORT FIELDS=(1,30,CH,A)
  OUTREC BUILD=(31,100)

TRAN=LTOU transforma letras minúsculas em maiúsculas na chave de trabalho.


40. Concatenando vários arquivos de entrada

É possível concatenar datasets no SORTIN:

//SORTIN DD DSN=VAGNER.ARQ.JANEIRO,DISP=SHR
//       DD DSN=VAGNER.ARQ.FEVEREIRO,DISP=SHR
//       DD DSN=VAGNER.ARQ.MARCO,DISP=SHR

O SORT enxerga a concatenação como um fluxo de entrada.

Mas os datasets precisam ser compatíveis:

  • organização adequada;

  • atributos compatíveis;

  • layouts coerentes;

  • interpretação correta do registro.

Não concatene arquivos apenas porque possuem o mesmo LRECL.

Dois arquivos de 100 bytes podem ter layouts completamente diferentes.


41. Usando GDG

SORT e GDG formam uma dupla clássica.

//SORTIN DD DSN=VAGNER.MOVIMENTO.DIARIO(0),DISP=SHR
//SORTOUT DD DSN=VAGNER.MOVIMENTO.ORDENADO(+1),
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(20,10),RLSE),
//            UNIT=SYSDA

Cuidados:

  • (0) refere-se normalmente à geração atual catalogada;

  • (+1) cria uma nova geração;

  • confirme o momento de catalogação;

  • lembre-se de que referências relativas podem assumir significado diferente conforme novas gerações são criadas no mesmo job.

Em jobs complexos, utilizar o mesmo (+1) em passos posteriores exige conhecimento preciso das regras de resolução do GDG.


42. Usando SORT com VSAM

DFSORT pode trabalhar com diferentes tipos de entrada e saída, inclusive VSAM em cenários suportados.

Mas o recruta deve tomar cuidado com:

  • organização do cluster;

  • chave VSAM;

  • REUSE;

  • abertura para entrada e saída;

  • tentativa de usar o mesmo cluster simultaneamente;

  • tamanho máximo e médio do registro;

  • arquivos variáveis;

  • códigos de retorno;

  • necessidade de descarregar e recarregar o cluster.

A própria documentação alerta que determinadas combinações de mesmo dataset VSAM como entrada e saída exigem condições específicas; caso contrário, o DFSORT encerra a operação. (IBM)

Em muitos processos, é mais seguro:

  1. descarregar;

  2. transformar;

  3. validar;

  4. carregar em novo cluster;

  5. trocar nomes ou executar procedimento controlado de substituição.


43. Return codes: o boletim da missão

Os códigos exatos devem ser confirmados pelas mensagens do produto e pelos padrões da instalação.

Como regra prática:

RC 0

Operação concluída normalmente.

RC 4

Aviso ou condição não fatal.

Pode indicar, por exemplo:

  • situação informativa;

  • condição programada;

  • comportamento que exige atenção.

RC 8 ou superior

Normalmente indica falha ou condição relevante.

Mas nunca interprete apenas o número.

Leia as mensagens:

ICE...

A mensagem detalhada é mais importante que o return code isolado.

O return code diz que alguém caiu. A mensagem diz onde estava a emboscada.


44. Erros clássicos do recruta

44.1 Posição errada

SORT FIELDS=(10,5,CH,A)

Mas o campo realmente começa em 11.

44.2 Esquecer o RDW em arquivo VB

Usa posição 1 quando deveria considerar a posição 5.

44.3 Tratar packed como texto

SORT FIELDS=(45,6,CH,A)

quando deveria ser:

SORT FIELDS=(45,6,PD,A)

44.4 Confundir tamanho lógico com tamanho físico

PIC S9(9)V99 COMP-3 não ocupa 11 bytes.

44.5 Usar posição original após INREC

O INREC muda o mapa do terreno.

44.6 Usar posição reformada antes do momento correto

Cada comando enxerga o registro em uma etapa específica.

44.7 Eliminar duplicados sem definir qual registro manter

SUM FIELDS=NONE não conhece a regra de negócio.

44.8 Sortear um arquivo que já estava ordenado

Talvez MERGE, COPY ou nenhuma operação fosse necessária.

44.9 Criar cinco passos quando um OUTFIL bastava

Mais passos significam:

  • mais I/O;

  • mais datasets;

  • mais pontos de falha;

  • mais JCL;

  • mais dificuldade de restart.

44.10 Esquecer registros vazios ou curtos em VB

Campos além do tamanho efetivo do registro podem causar problemas.

44.11 Confiar na ordem visual

Dados exibidos no ISPF podem esconder:

  • bytes não imprimíveis;

  • sinais packed;

  • zeros;

  • caracteres especiais;

  • diferenças entre espaço e low-value.

44.12 Ignorar a codificação

ASCII e EBCDIC não possuem a mesma sequência de caracteres.


45. Método passo a passo para escrever um SORT seguro

Passo 1 — Defina a missão

Escreva em português claro:

“Ler o arquivo de movimentos, selecionar transações aprovadas, ordenar por agência e conta, consolidar valores e produzir arquivos separados por região.”

Se você não consegue explicar a missão, ainda não está pronto para escrever o SYSIN.

Passo 2 — Obtenha o layout

Consulte:

  • copybook;

  • documentação;

  • amostra;

  • atributos do dataset;

  • origem do arquivo.

Passo 3 — Faça uma tabela de posições

CampoInícioTamanhoFormato
Agência14CH
Conta510CH
Data158CH
Valor236PD
Status291CH

Passo 4 — Examine dados reais

Use:

  • ISPF Browse;

  • modo hexadecimal;

  • File-AID ou ferramenta equivalente;

  • pequeno unload;

  • ICETOOL DISPLAY;

  • programa de diagnóstico.

Passo 5 — Faça primeiro uma amostra

Não comece testando com 500 milhões de registros.

Crie um dataset pequeno contendo:

  • menor chave;

  • maior chave;

  • duplicados;

  • valor zero;

  • valor negativo;

  • registro inválido;

  • status desconhecido;

  • primeiro e último grupo;

  • caracteres especiais.

Passo 6 — Escreva a operação mínima

Comece:

SORT FIELDS=COPY

Depois acrescente:

INCLUDE
INREC
SORT
SUM
OUTREC
OUTFIL

uma camada por vez.

Passo 7 — Valide contagens

Compare:

LIDOS
- DESCARTADOS
= GRAVADOS

Em múltiplas saídas:

ATIVOS + INATIVOS + CANCELADOS + REJEITADOS
= TOTAL DE ENTRADA

Passo 8 — Confirme a ordem

Verifique:

  • primeira chave;

  • última chave;

  • transição entre grupos;

  • duplicados;

  • chaves negativas ou especiais;

  • dados nulos ou espaços.

Passo 9 — Leia todas as mensagens

Não aceite apenas “RC=0”.

Veja:

  • opções usadas;

  • quantidade de registros;

  • alocação;

  • avisos;

  • mensagens de performance;

  • possíveis overrides da instalação.

As opções padrão podem ser alteradas pelos administradores do ambiente, portanto dois sistemas podem executar o mesmo JCL com defaults diferentes. (IBM)

Passo 10 — Documente a regra

Inclua comentários:

//* ORDENA MOVIMENTOS POR AGENCIA, CONTA E DATA
//* MANTEM O MOVIMENTO MAIS RECENTE POR CONTA
//* CAMPO DATA: POS 15, 8 BYTES, AAAAMMDD

O comentário de hoje é o mapa que salvará o recruta de amanhã.


46. Exemplo completo de produção

Missão

Ler movimentos bancários com layout FB de 80 bytes:

CampoPosiçãoTamanhoFormato
Agência14CH
Conta510CH
Data158CH
Hora236CH
Tipo291CH
Valor306PD
Status361CH
Restante3744CH

Requisitos:

  1. aceitar apenas status A;

  2. ordenar por agência e conta;

  3. dentro da conta, colocar data e hora mais recentes primeiro;

  4. criar saída de crédito;

  5. criar saída de débito;

  6. criar saída para tipos desconhecidos;

  7. adicionar data de processamento;

  8. produzir contagem no rodapé.

//MOVSORT  JOB (ACCT),'BELLACOSA',
//             CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//*
//* PLATOON DO BATCH - CLASSIFICACAO DE MOVIMENTOS
//*
//ORDENA   EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=VAGNER.MOVIMENTO.BRUTO,DISP=SHR
//*
//CREDITO  DD DSN=VAGNER.MOVIMENTO.CREDITO,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(50,20),RLSE),
//            DCB=(RECFM=FB,LRECL=110,BLKSIZE=0)
//*
//DEBITO   DD DSN=VAGNER.MOVIMENTO.DEBITO,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(50,20),RLSE),
//            DCB=(RECFM=FB,LRECL=110,BLKSIZE=0)
//*
//REJEITA  DD DSN=VAGNER.MOVIMENTO.REJEITADO,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(10,5),RLSE),
//            DCB=(RECFM=FB,LRECL=110,BLKSIZE=0)
//*
//SYSIN    DD *
  INCLUDE COND=(36,1,CH,EQ,C'A')

  SORT FIELDS=(1,4,CH,A,
               5,10,CH,A,
               15,8,CH,D,
               23,6,CH,D)

  OUTREC BUILD=(1,80,
                C' PROC=',
                DATE1,
                C' ',
                TIME1)

  OUTFIL FNAMES=CREDITO,
         INCLUDE=(29,1,CH,EQ,C'C'),
         TRAILER1=(C'TOTAL CREDITOS: ',COUNT)

  OUTFIL FNAMES=DEBITO,
         INCLUDE=(29,1,CH,EQ,C'D'),
         TRAILER1=(C'TOTAL DEBITOS: ',COUNT)

  OUTFIL FNAMES=REJEITA,
         SAVE,
         TRAILER1=(C'TOTAL REJEITADOS: ',COUNT)
/*

Este exemplo precisa ser adaptado aos padrões locais, especialmente:

  • sintaxe de data e hora desejada;

  • LRECL final;

  • formato dos trailers;

  • atributos de saída;

  • regras de negócio;

  • quantidade estimada de espaço;

  • produto SORT instalado.


47. Exemplo com dataset temporário e restart lógico

Imagine um job em três etapas:

  1. selecionar e normalizar;

  2. cruzar com cadastro;

  3. gerar relatórios.

//PREPARA  EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=VAGNER.TRANSACOES.BRUTAS,DISP=SHR
//SORTOUT  DD DSN=&&NORMAL,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(CYL,(100,50)),
//            DCB=(RECFM=FB,LRECL=60,BLKSIZE=0)
//SYSIN    DD *
  INCLUDE COND=(100,1,CH,EQ,C'A')

  INREC BUILD=(1,10,
               20,8,
               50,6,
               120,35,
               C'N')
               
  SORT FIELDS=(1,10,CH,A)
/*
//*
//CRUZA    EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTJNF1 DD DSN=&&NORMAL,DISP=(OLD,DELETE)
//SORTJNF2 DD DSN=VAGNER.CLIENTES.MESTRE,DISP=SHR
//SORTOUT  DD DSN=&&CRUZADO,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(CYL,(100,50)),
//            DCB=(RECFM=FB,LRECL=120,BLKSIZE=0)
//SYSIN    DD *
  JOINKEYS FILE=F1,FIELDS=(1,10,A)
  JOINKEYS FILE=F2,FIELDS=(1,10,A)

  JOIN UNPAIRED,F1

  REFORMAT FIELDS=(F1:1,60,
                   F2:11,59,
                   ?)

  SORT FIELDS=COPY
/*
//*
//RELAT    EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=&&CRUZADO,DISP=(OLD,DELETE)
//OK       DD DSN=VAGNER.RELATORIO.OK,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(20,10),RLSE)
//SEMCLI   DD DSN=VAGNER.RELATORIO.SEMCLIENTE,
//            DISP=(NEW,CATLG,DELETE),
//            UNIT=SYSDA,
//            SPACE=(CYL,(10,5),RLSE)
//SYSIN    DD *
  SORT FIELDS=COPY

  OUTFIL FNAMES=OK,
         INCLUDE=(120,1,CH,EQ,C'B')

  OUTFIL FNAMES=SEMCLI,
         SAVE
/*

Nesse tipo de fluxo, os temporários fazem sentido porque representam fronteiras claras entre etapas diferentes.

Entretanto, se restart for requisito crítico, um dataset permanente controlado por geração ou ciclo pode ser preferível. Datasets &&TEMP desaparecem ao final do job e podem não servir como checkpoint reutilizável em uma nova submissão.


48. Curiosidades do acampamento

Curiosidade 1 — O SORT pode substituir pequenos programas COBOL

Muitos programas escritos apenas para:

  • filtrar;

  • mover campos;

  • somar;

  • eliminar duplicados;

  • separar arquivos;

  • inserir constantes;

poderiam ser substituídos por DFSORT.

Isso não significa que todo programa deve virar SORT.

Mas antes de criar um novo módulo COBOL, pergunte:

Esta lógica é transformação de registros ou verdadeira regra de negócio?

Curiosidade 2 — Um SORT bem escrito pode reduzir janelas batch

Eliminar leituras, escritas e passos intermediários pode representar grande economia em ambientes de alto volume.

Curiosidade 3 — “Arquivo já ordenado” é uma afirmação perigosa

Um arquivo pode estar:

  • visualmente ordenado;

  • ordenado apenas pela primeira chave;

  • ordenado em ASCII, não EBCDIC;

  • ordenado crescentemente quando o programa espera decrescente;

  • ordenado sem estabilidade;

  • ordenado antes de uma concatenação desordenada.

Curiosidade 4 — A menor mudança no layout pode destruir o resultado

Adicionar dois bytes no início do arquivo desloca todas as posições posteriores.

O SORT continuará executando.

Talvez retorne RC 0.

E produzirá dados perfeitamente classificados pela coluna errada.

Esse é um dos cenários mais perigosos: o erro silencioso.

Curiosidade 5 — O nome ICETOOL combina com o produto

Enquanto o CPD esquenta, o ICETOOL entra em cena com seus comandos começando por ICE.

Curiosidade 6 — Nem todo RC 4 é derrota

Às vezes é uma condição planejada para controlar o fluxo do job.

Curiosidade 7 — O SORT conhece o registro em diferentes versões

O registro visto por INCLUDE pode não ser o mesmo visto por SORT, que pode não ser o mesmo visto por OUTFIL.

Compreender a ordem de processamento é metade do treinamento.


49. Easter eggs Bellacosa

Easter egg 1 — O sargento Elias dos datasets

INCLUDE é o soldado que olha cada registro e decide:

— Você vem conosco.

OMIT olha o mesmo registro e diz:

— Você fica.

Easter egg 2 — O sargento Barnes do batch

SUM FIELDS=NONE não negocia com duplicados.

Ele escolhe um sobrevivente e remove os demais.

Por isso, nunca o deixe decidir sozinho quando a regra de negócio exige saber exatamente qual registro deve permanecer.

Easter egg 3 — A floresta não é o que parece

No arquivo VB, os quatro bytes do RDW estão sempre à frente.

Invisíveis.

Esperando o recruta esquecer que eles existem.

Easter egg 4 — “Adagio for Strings” no CPD

Quando o job termina com RC 16 às cinco e cinquenta da manhã e a janela batch fecha às seis, algum operador deveria colocar Adagio for Strings para tocar no data center.

Easter egg 5 — Ninguém luta sozinho

O SORT prepara dados para:

  • COBOL;

  • CICS;

  • Db2;

  • IMS;

  • VSAM;

  • relatórios;

  • reconciliações;

  • fechamento contábil;

  • processamento de cartões;

  • folhas de pagamento.

Ele raramente recebe aplausos.

Mas, quando falha, todo o exército percebe.


50. Checklist do recruta antes de liberar o JCL

Dataset

  • Confirmei RECFM.

  • Confirmei LRECL.

  • Confirmei se o arquivo é FB ou VB.

  • Confirmei se há RDW.

  • Confirmei organização e volume.

  • Confirmei se a entrada pode estar vazia.

Layout

  • Calculei as posições.

  • Verifiquei campos packed.

  • Verifiquei campos binários.

  • Examinei dados em hexadecimal.

  • Confirmei sinais e decimais implícitos.

Regras

  • Sei quais registros entram.

  • Sei quais registros são eliminados.

  • Sei como duplicados devem ser tratados.

  • Sei qual registro deve sobreviver.

  • Sei a ordem exata das chaves.

  • Sei quais saídas serão produzidas.

Performance

  • Filtrei antes de ordenar.

  • Reduzi registros quando possível.

  • Evitei passos redundantes.

  • Avaliei OUTFIL.

  • Avaliei MERGE.

  • Não copiei SORTWKnn sem justificativa.

  • Estimei espaço de saída.

Validação

  • Testei arquivo vazio.

  • Testei um registro.

  • Testei duplicados.

  • Testei valores negativos.

  • Testei chave mínima e máxima.

  • Conferi contagens.

  • Conferi primeiro e último registro.

  • Li as mensagens completas.

  • Documentei as posições.


51. Dez comandos que todo recruta deve memorizar

//* 1. ORDENAR
  SORT FIELDS=(1,10,CH,A)
//* 2. COPIAR
  SORT FIELDS=COPY
//* 3. INCLUIR
  INCLUDE COND=(20,1,CH,EQ,C'A')
//* 4. OMITIR
  OMIT COND=(20,1,CH,EQ,C'C')
//* 5. REMOVER DUPLICADOS
  SORT FIELDS=(1,10,CH,A)
  SUM FIELDS=NONE
//* 6. SOMAR
  SORT FIELDS=(1,4,CH,A)
  SUM FIELDS=(20,6,PD)
//* 7. REFORMATAR ANTES
  INREC BUILD=(10,5,1,9)
//* 8. REFORMATAR DEPOIS
  OUTREC BUILD=(1,10,C'|',11,20)
//* 9. MULTIPLAS SAIDAS
  OUTFIL FNAMES=SAIDA1,INCLUDE=(1,1,CH,EQ,C'A')
  OUTFIL FNAMES=SAIDA2,SAVE
//* 10. CRUZAR ARQUIVOS
  JOINKEYS FILE=F1,FIELDS=(1,10,A)
  JOINKEYS FILE=F2,FIELDS=(1,10,A)

52. A última lição do pelotão

O recruta submeteu o job às cinco e doze.

O JES aceitou.

O passo iniciou.

Milhões de registros entraram no DFSORT como soldados perdidos em uma floresta fechada. Contas, agências, datas e valores atravessaram memória, buffers e áreas de trabalho.

Alguns registros foram eliminados.

Outros foram reformatados.

Duplicados desapareceram.

Valores foram consolidados.

Três arquivos surgiram na saída, cada um rigorosamente organizado.

Às cinco e vinte e nove, o job terminou:

MAXCC=0000

O veterano examinou as mensagens, conferiu as contagens e perguntou:

— Quantos registros entraram?

O recruta respondeu.

— Quantos foram rejeitados?

O recruta respondeu novamente.

— Qual duplicado foi mantido?

Dessa vez, o jovem mostrou a regra de data e hora descendente.

O veterano finalmente assentiu.

Não porque o job havia terminado com RC 0.

Mas porque o recruta sabia explicar por que o resultado estava correto.

No mainframe, qualquer um pode copiar uma instrução:

SORT FIELDS=(1,10,CH,A)

O verdadeiro profissional entende:

  • quais bytes estão sendo comparados;

  • como estão representados;

  • em que momento o registro foi transformado;

  • quanto espaço será utilizado;

  • qual duplicado sobreviverá;

  • o que acontecerá quando o arquivo estiver vazio;

  • como provar que a saída está certa.

Essa é a diferença entre executar um SORT e comandar o SORT.

E quando a madrugada cair sobre o CPD, os discos começarem a girar e milhões de registros marcharem para dentro do batch, lembre-se:

O caos pode ter milhões de linhas.
Mas basta uma chave correta para colocá-lo em ordem.

Bem-vindo ao pelotão, recruta.


Sem comentários:

Enviar um comentário

Vagner Renato Bellacosa, IBM Champion e especialista em IBM Mainframe
IBM Z17 SYSTEM ONLINE
Sobre o autor

Vagner Renato Bellacosa

IBM Champion 2026 • Especialista em IBM Mainframe

Vagner Renato Bellacosa trabalha com IBM Mainframe desde 1988 , é IBM Champion e especialista em COBOL, CICS, Db2, z/OS e IBM Z. Compartilha experiências profissionais, conhecimento técnico, história da computação e práticas do universo mainframe para aproximar novas gerações das tecnologias que sustentam empresas, bancos e governos ao redor do mundo.

IBM Champion IBM Z COBOL CICS Db2 z/OS Mainframe desde 1988
GitHub LinkedIn
Inicializando conteúdo...