1. Tipos de dados no DB2
Escolher o tipo certo impacta diretamente o espaço em disco, a performance de queries e o comportamento de comparações. Abaixo os tipos mais usados em ambientes bancários.
Numéricos
| Tipo | Tamanho | Faixa | Uso típico |
|---|---|---|---|
SMALLINT | 2 bytes | -32.768 a 32.767 | Flags, códigos pequenos |
INTEGER | 4 bytes | ±2,1 bilhões | IDs, contadores |
BIGINT | 8 bytes | ±9,2 quintilhões | Chaves de alto volume |
DECIMAL(p,s) | ⌈(p+1)/2⌉ bytes | p até 31 dígitos | Valores monetários |
FLOAT | 8 bytes | ponto flutuante | Cálculos científicos |
DECIMAL(15,2) para valores em reais com centavos.
Caractere
| Tipo | Comprimento | Preenchimento | Uso |
|---|---|---|---|
CHAR(n) | Fixo — 1 a 254 | Espaços à direita | CPF, agência, código fixo |
VARCHAR(n) | Variável — até 32.704 | Nenhum | Nome, endereço, descrição |
CLOB(n) | Até 2 GB | — | Textos longos, logs |
CHAR(10) com VARCHAR(10) pode dar resultados inesperados porque o CHAR é sempre preenchido com espaços. Use o mesmo tipo nas duas colunas de um JOIN.
Data e hora
| Tipo | Formato interno | Exemplo |
|---|---|---|
DATE | 4 bytes | '2026-06-15' |
TIME | 3 bytes | '14:30:00' |
TIMESTAMP | 10 bytes | '2026-06-15-14.30.00.000000' |
'2026-06-15-14.30.00.000000'. Isso pega muita gente que vem de outros bancos.
2. Registros especiais
Registros especiais são variáveis globais de somente leitura mantidas pelo DB2. Você os usa diretamente em qualquer expressão SQL, sem declarar nada.
-- Data e hora atuais SELECT CURRENT DATE -- DATE de hoje , CURRENT TIME -- TIME agora , CURRENT TIMESTAMP -- TIMESTAMP completo FROM SYSIBM.SYSDUMMY1; -- Contexto da sessão SELECT CURRENT SCHEMA -- schema padrão ativo , CURRENT SERVER -- nome do subsistema DB2 , USER -- ID do usuário conectado FROM SYSIBM.SYSDUMMY1;
DUAL do Oracle.
3. Funções de string
-- SUBSTR(string, início, comprimento) SELECT SUBSTR('LADYCOBOL', 1, 4) -- 'LADY' -- LENGTH — comprimento real (sem espaços extras em VARCHAR) SELECT LENGTH('COBOL ') -- 8 (CHAR conta os espaços) -- STRIP / LTRIM / RTRIM — remove espaços SELECT STRIP(' COBOL ') -- 'COBOL' SELECT LTRIM(' COBOL') -- 'COBOL' SELECT RTRIM('COBOL ') -- 'COBOL' -- UPPER / LOWER SELECT UPPER('lady cobol') -- 'LADY COBOL' SELECT LOWER('LADY COBOL') -- 'lady cobol' -- CONCAT ou operador || SELECT 'LADY' || ' ' || 'COBOL' -- 'LADY COBOL' SELECT CONCAT('LADY', 'COBOL') -- 'LADYCOBOL' -- POSSTR — posição de substring (começa em 1, retorna 0 se não achar) SELECT POSSTR('LADYCOBOL', 'COBOL') -- 5 -- REPLACE — substitui todas as ocorrências SELECT REPLACE('01/06/2026', '/', '-') -- '01-06-2026' -- LEFT / RIGHT SELECT LEFT('AGENCIA001', 7) -- 'AGENCIA' SELECT RIGHT('AGENCIA001', 3) -- '001'
4. Funções de data e hora
SELECT YEAR(CURRENT DATE) -- 2026 , MONTH(CURRENT DATE) -- 6 , DAY(CURRENT DATE) -- 15 , HOUR(CURRENT TIMESTAMP) -- hora (0–23) , MINUTE(CURRENT TIMESTAMP) -- minuto , SECOND(CURRENT TIMESTAMP) -- segundo FROM SYSIBM.SYSDUMMY1;
-- Somar/subtrair com DURATION (n DAYS / MONTHS / YEARS) SELECT CURRENT DATE + 30 DAYS -- daqui 30 dias , CURRENT DATE - 1 MONTH -- mês passado , CURRENT DATE + 1 YEAR -- ano que vem FROM SYSIBM.SYSDUMMY1; -- Diferença entre datas em dias (DAYS retorna inteiro) SELECT DAYS(CURRENT DATE) - DAYS('2026-01-01') FROM SYSIBM.SYSDUMMY1; -- dias desde 01/01/2026 -- TIMESTAMPDIFF — diferença entre timestamps em unidades variadas -- Unidade: 16=min, 32=hora, 64=dia, 128=semana, 256=mês, 512=trimestre, 1024=ano SELECT TIMESTAMPDIFF(64, CHAR(CURRENT TIMESTAMP - '2026-01-01-00.00.00.000000')) FROM SYSIBM.SYSDUMMY1; -- diferença em dias
-- DATE() e TIME() convertem TIMESTAMP SELECT DATE(CURRENT TIMESTAMP) -- parte de data , TIME(CURRENT TIMESTAMP) -- parte de hora FROM SYSIBM.SYSDUMMY1; -- CHAR converte DATE para string com formato ISO, EUR, USA ou LOCAL SELECT CHAR(CURRENT DATE, ISO) -- '2026-06-15' , CHAR(CURRENT DATE, EUR) -- '15.06.2026' , CHAR(CURRENT DATE, USA) -- '06/15/2026' FROM SYSIBM.SYSDUMMY1;
5. Funções numéricas
-- ABS — valor absoluto SELECT ABS(-150.75) -- 150.75 -- MOD — resto da divisão SELECT MOD(17, 5) -- 2 -- INTEGER — trunca para inteiro (não arredonda) SELECT INTEGER(3.9) -- 3 -- DECIMAL(valor, precisão, escala) — converte com precisão controlada SELECT DECIMAL(1234.567, 10, 2) -- 1234.57 (arredonda) -- ROUND — arredondamento SELECT ROUND(3.456, 2) -- 3.46 SELECT ROUND(3.454, 2) -- 3.45
6. Funções de conversão
Conversão entre tipos é frequente em COBOL+DB2 — variáveis host são declaradas como numérico ou alfanumérico e nem sempre o tipo coincide com o da coluna.
-- CHAR — converte número ou data para string SELECT CHAR(12345) -- '12345' SELECT CHAR(9.99) -- '9.99' SELECT CHAR(CURRENT DATE, ISO) -- '2026-06-15' -- DIGITS — número para string com zeros à esquerda (sem sinal) SELECT DIGITS(42) -- '0000000042' (para DECIMAL(10,0)) -- HEX — representação hexadecimal SELECT HEX('A') -- 'C1' (EBCDIC) -- INTEGER / BIGINT / SMALLINT — string ou decimal para inteiro SELECT INTEGER('2026') -- 2026 -- CAST — conversão explícita (mais portável entre bancos) SELECT CAST('2026-06-15' AS DATE) SELECT CAST(SALDO AS DECIMAL(15,2))
DIGITS quando precisar de número com zeros à esquerda sem ponto decimal — muito comum para formatar campos de relatório em COBOL.
7. Tratamento de nulo
NULL no SQL significa ausência de valor — não é zero, não é espaço, não é falso. Qualquer expressão com NULL resulta em NULL.
-- COALESCE — retorna o primeiro valor não nulo da lista SELECT COALESCE(EMAIL, TELEFONE, 'SEM CONTATO') FROM CLIENTES; -- VALUE — sinônimo de COALESCE no DB2 (função proprietária IBM) SELECT VALUE(LIMITE_CREDITO, 0) FROM CONTAS; -- NULLIF — retorna NULL se os dois valores forem iguais SELECT NULLIF(DIVISOR, 0) -- evita divisão por zero -- Verificação correta de nulo (nunca use = NULL) SELECT * FROM CONTAS WHERE LIMITE_CREDITO IS NULL SELECT * FROM CONTAS WHERE LIMITE_CREDITO IS NOT NULL
WHERE COLUNA = NULL nunca retorna linhas no DB2. NULL não é igual a nada — nem a outro NULL. Use sempre IS NULL ou IS NOT NULL.
8. Funções de agregação
SELECT COUNT(*) -- total de linhas (inclui NULLs) , COUNT(EMAIL) -- linhas onde EMAIL não é NULL , COUNT(DISTINCT AGENCIA) -- agências únicas , SUM(SALDO) -- soma (ignora NULL) , AVG(SALDO) -- média (ignora NULL) , MIN(DT_ABERTURA) -- data mais antiga , MAX(SALDO) -- maior saldo FROM CONTAS;
-- Saldo total por agência, só para agências com mais de 100 contas SELECT COD_AGENCIA , COUNT(*) AS QTD_CONTAS , SUM(SALDO) AS SALDO_TOTAL , AVG(SALDO) AS SALDO_MEDIO FROM CONTAS GROUP BY COD_AGENCIA HAVING COUNT(*) > 100 ORDER BY SALDO_TOTAL DESC;
WHERE filtra linhas antes de agrupar. HAVING filtra grupos depois de agrupar. Se você quer filtrar por valor calculado (SUM, COUNT...) use HAVING — WHERE não tem acesso a esses valores.
9. Diferenças do SQL padrão
Quem vem de MySQL, PostgreSQL ou SQL Server vai encontrar algumas surpresas no DB2 para z/OS.
FETCH FIRST em vez de LIMIT
-- DB2 — limita linhas retornadas SELECT * FROM TRANSACOES ORDER BY DT_TRANSACAO DESC FETCH FIRST 10 ROWS ONLY; -- NÃO EXISTE no DB2 z/OS: -- SELECT * FROM TRANSACOES LIMIT 10 ← MySQL/PostgreSQL -- SELECT TOP 10 * FROM TRANSACOES ← SQL Server
Sem tipo BOOLEAN
O DB2 para z/OS não tem tipo BOOLEAN. Condições lógicas são representadas com SMALLINT (0/1) ou CHAR(1) ('S'/'N'). Flags no banco geralmente são colunas CHAR(1) NOT NULL DEFAULT 'N'.
Concatenação com ||
-- DB2 usa || para concatenar strings SELECT NOME || ' ' || SOBRENOME AS NOME_COMPLETO FROM CLIENTES; -- Cuidado: NULL || 'algo' = NULL SELECT COALESCE(NOME, '') || ' ' || COALESCE(SOBRENOME, '') FROM CLIENTES;
Aritmética de datas com DURATION
-- DB2 — sintaxe própria para aritmética de datas SELECT DT_VENCIMENTO + 5 DAYS -- adiciona 5 dias , DT_VENCIMENTO - 1 MONTH -- subtrai 1 mês FROM TITULOS; -- Subtração entre duas datas resulta em DURATION (não em INTEGER) -- Para obter inteiro use a função DAYS() SELECT DAYS(DT_VENCIMENTO) - DAYS(CURRENT DATE) AS DIAS_RESTANTES FROM TITULOS;
WITH (Common Table Expression)
WITH CONTAS_ATIVAS AS ( SELECT COD_CLIENTE, SALDO FROM CONTAS WHERE STATUS = 'A' ) SELECT C.NOME, CA.SALDO FROM CLIENTES C JOIN CONTAS_ATIVAS CA ON C.COD_CLIENTE = CA.COD_CLIENTE WHERE CA.SALDO > 1000;
10. JOINs e subqueries
-- INNER JOIN — só linhas com correspondência nos dois lados SELECT C.NOME, T.VL_TRANSACAO, T.DT_TRANSACAO FROM CLIENTES C INNER JOIN TRANSACOES T ON C.COD_CLIENTE = T.COD_CLIENTE; -- LEFT OUTER JOIN — todos os clientes, mesmo sem transações SELECT C.NOME, T.VL_TRANSACAO FROM CLIENTES C LEFT OUTER JOIN TRANSACOES T ON C.COD_CLIENTE = T.COD_CLIENTE; -- FULL OUTER JOIN — todos de ambos os lados SELECT C.NOME, T.VL_TRANSACAO FROM CLIENTES C FULL OUTER JOIN TRANSACOES T ON C.COD_CLIENTE = T.COD_CLIENTE;
-- IN com subquery SELECT * FROM CLIENTES WHERE COD_CLIENTE IN (SELECT COD_CLIENTE FROM CONTAS WHERE SALDO > 50000); -- EXISTS — eficiente para checar existência (para quando acha a 1ª linha) SELECT * FROM CLIENTES C WHERE EXISTS ( SELECT 1 FROM CONTAS A WHERE A.COD_CLIENTE = C.COD_CLIENTE AND A.STATUS = 'INADIMPLENTE'); -- NOT EXISTS — clientes sem conta inadimplente SELECT * FROM CLIENTES C WHERE NOT EXISTS ( SELECT 1 FROM CONTAS A WHERE A.COD_CLIENTE = C.COD_CLIENTE AND A.STATUS = 'INADIMPLENTE');
-- UNION ALL — une dois resultados sem remover duplicatas (mais rápido) SELECT COD_CLIENTE, 'ATIVO' AS ORIGEM FROM CONTAS_ATIVAS UNION ALL SELECT COD_CLIENTE, 'INATIVO' AS ORIGEM FROM CONTAS_INATIVAS; -- UNION — remove duplicatas (faz sort interno — mais caro) SELECT COD_CLIENTE FROM CONTAS_ATIVAS UNION SELECT COD_CLIENTE FROM CONTAS_INATIVAS;
11. Exemplos práticos
SELECT CASE WHEN SALDO < 0 THEN 'NEGATIVO' WHEN SALDO = 0 THEN 'ZERADO' WHEN SALDO BETWEEN 1 AND 999 THEN 'BAIXO' WHEN SALDO BETWEEN 1000 AND 9999 THEN 'MEDIO' ELSE 'ALTO' END AS FAIXA , COUNT(*) AS QTD , SUM(SALDO) AS TOTAL FROM CONTAS GROUP BY CASE WHEN SALDO < 0 THEN 'NEGATIVO' WHEN SALDO = 0 THEN 'ZERADO' WHEN SALDO BETWEEN 1 AND 999 THEN 'BAIXO' WHEN SALDO BETWEEN 1000 AND 9999 THEN 'MEDIO' ELSE 'ALTO' END ORDER BY TOTAL DESC;
SELECT C.NOME , COUNT(A.COD_CONTA) AS QTD_CONTAS , SUM(A.SALDO) AS SALDO_TOTAL , CHAR(CURRENT DATE, ISO) AS DT_CONSULTA FROM CLIENTES C JOIN CONTAS A ON C.COD_CLIENTE = A.COD_CLIENTE WHERE A.STATUS = 'A' GROUP BY C.NOME HAVING SUM(A.SALDO) > 0 ORDER BY SALDO_TOTAL DESC FETCH FIRST 5 ROWS ONLY;