Tabelas Internas
Tipos de Tabelas
STANDARD TABLE (mais comum)
" Declaração de tipo
TYPES ty_t_clientes TYPE STANDARD TABLE OF ty_cliente WITH DEFAULT KEY.
" Declaração inline
DATA lt_produtos TYPE STANDARD TABLE OF ty_produto WITH EMPTY KEY.
" Com chave específica
DATA lt_pedidos TYPE STANDARD TABLE OF ty_pedido
WITH NON-UNIQUE KEY numero.
" Múltiplas chaves
DATA lt_vendas TYPE STANDARD TABLE OF ty_venda
WITH NON-UNIQUE KEY pais ano mes
WITH UNIQUE SORTED KEY by_id COMPONENTS id.
SORTED TABLE
" Sempre ordenada pela chave
DATA lt_clientes TYPE SORTED TABLE OF ty_cliente
WITH UNIQUE KEY id.
" Chave não única
DATA lt_produtos TYPE SORTED TABLE OF ty_produto
WITH NON-UNIQUE KEY categoria preco.
" Acesso binário automático (mais rápido)
HASHED TABLE
" Acesso hash (muito rápido para leitura)
DATA lt_cache TYPE HASHED TABLE OF ty_cache
WITH UNIQUE KEY id.
" Ideal para lookups frequentes
DATA lt_config TYPE HASHED TABLE OF ty_config
WITH UNIQUE KEY chave.
" Não permite índice numérico
" Não pode ser ordenada
Inicialização e Preenchimento
VALUE Constructor
" Tabela vazia
DATA(lt_empty) = VALUE ty_t_produtos( ).
" Com valores iniciais
DATA(lt_numeros) = VALUE ty_t_int( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ).
" Tabela de estruturas
DATA(lt_clientes) = VALUE ty_t_clientes(
( id = 1 nome = 'João Silva' cidade = 'Lisboa' )
( id = 2 nome = 'Maria Santos' cidade = 'Porto' )
( id = 3 nome = 'Pedro Costa' cidade = 'Braga' )
).
" Com FOR (loop inline)
DATA(lt_quadrados) = VALUE ty_t_int(
FOR i = 1 THEN i + 1 WHILE i <= 10
( i * i )
).
APPEND
" Adicionar estrutura
DATA ls_cliente TYPE ty_cliente.
ls_cliente = VALUE #( id = 1 nome = 'Ana' ).
APPEND ls_cliente TO lt_clientes.
" Inline
APPEND VALUE #( id = 2 nome = 'Bruno' ) TO lt_clientes.
" Múltiplas linhas
APPEND LINES OF lt_novos TO lt_clientes.
" Com FROM/TO
APPEND LINES OF lt_origem FROM 5 TO 10 TO lt_destino.
INSERT
" Inserir em posição específica
DATA(ls_novo) = VALUE ty_cliente( id = 99 nome = 'Novo Cliente' ).
INSERT ls_novo INTO lt_clientes INDEX 1.
" Inserir inline
INSERT VALUE #( id = 100 nome = 'Outro' ) INTO lt_clientes INDEX 3.
" Inserir tabela em posição
INSERT LINES OF lt_novos INTO lt_clientes INDEX 5.
" Em sorted/hashed table (insere na posição correcta)
INSERT ls_cliente INTO TABLE lt_clientes_sorted.
Leitura de Dados
READ TABLE
" Por índice
READ TABLE lt_clientes INDEX 1 INTO DATA(ls_cliente).
IF sy-subrc = 0.
WRITE: / ls_cliente-nome.
ENDIF.
" Por chave
READ TABLE lt_clientes INTO ls_cliente WITH KEY id = 100.
" Com ASSIGNING (mais eficiente)
READ TABLE lt_produtos ASSIGNING FIELD-SYMBOL(<ls_prod>) WITH KEY id = 200.
IF sy-subrc = 0.
<ls_prod>-preco = <ls_prod>-preco * '1.1'.
ENDIF.
" Com REFERENCE
READ TABLE lt_clientes REFERENCE INTO DATA(lr_cliente) WITH KEY id = 50.
" Busca binária (em sorted table)
READ TABLE lt_clientes_sorted INTO ls_cliente
WITH KEY id = 100 BINARY SEARCH.
Acesso Direto por []
" Por índice (pode causar dump se não existir)
DATA(ls_primeiro) = lt_clientes[ 1 ].
" Por chave
DATA(ls_cliente) = lt_clientes[ id = 100 ].
" Com KEY secundária
DATA(ls_venda) = lt_vendas[ KEY by_date data = lv_data ].
" Acesso a campo específico
DATA(lv_nome) = lt_clientes[ 1 ]-nome.
" Com DEFAULT (evita dump)
DATA(ls_safe) = VALUE #( lt_clientes[ id = 999 ]
DEFAULT VALUE #( id = 0 nome = 'Não encontrado' ) ).
" OPTIONAL - retorna referência ou null
TRY.
DATA(ls_opt) = lt_clientes[ id = 999 ].
CATCH cx_sy_itab_line_not_found.
" Tratar ausência
ENDTRY.
Modificação de Dados
MODIFY
" Modificar linha específica
DATA(ls_update) = VALUE ty_cliente( id = 1 nome = 'Nome Atualizado' ).
MODIFY TABLE lt_clientes FROM ls_update.
" Modificar por índice
MODIFY lt_clientes INDEX 1 FROM ls_update.
" Modificar dentro de loop
LOOP AT lt_produtos ASSIGNING FIELD-SYMBOL(<ls_prod>).
<ls_prod>-preco = <ls_prod>-preco * '1.1'.
ENDLOOP.
" MODIFY dentro de loop (menos eficiente)
LOOP AT lt_clientes INTO DATA(ls_cli).
ls_cli-ultima_atualizacao = sy-datum.
MODIFY lt_clientes FROM ls_cli.
ENDLOOP.
" Inline com TRANSPORTING
MODIFY lt_produtos FROM VALUE #( id = 100 stock = 50 )
TRANSPORTING stock WHERE id = 100.
DELETE
" Deletar por índice
DELETE lt_clientes INDEX 1.
" Deletar por chave
DELETE TABLE lt_clientes WITH TABLE KEY id = 100.
" Deletar com WHERE
DELETE lt_produtos WHERE stock = 0.
" Deletar dentro de loop
LOOP AT lt_clientes INTO DATA(ls_cli).
IF ls_cli-ativo = abap_false.
DELETE lt_clientes. " Deleta linha atual
ENDIF.
ENDLOOP.
" Deletar linhas adjacentes
DELETE lt_clientes FROM 5 TO 10.
" Limpar tabela completa
CLEAR lt_clientes.
" ou
lt_clientes = VALUE #( ).
" ou
REFRESH lt_clientes.
Ordenação
SORT
" Ordenação simples (ascendente)
SORT lt_clientes BY nome.
" Descendente
SORT lt_clientes BY nome DESCENDING.
" Múltiplos campos
SORT lt_vendas BY pais ASCENDING ano DESCENDING mes ASCENDING.
" Caso insensitivo
SORT lt_produtos BY descricao AS TEXT.
" Estável (mantém ordem original para iguais)
SORT lt_dados BY campo STABLE.
Filtros e Transformações
FILTER
" Filtrar com WHERE
DATA(lt_ativos) = FILTER #( lt_clientes WHERE ativo = abap_true ).
" Múltiplas condições
DATA(lt_premium) = FILTER #( lt_clientes
WHERE tipo = 'VIP'
AND saldo > 10000
AND ativo = abap_true
).
" EXCEPT - inverter filtro
DATA(lt_inativos) = FILTER #( lt_clientes EXCEPT WHERE ativo = abap_true ).
" Com chave secundária
DATA(lt_filtrado) = FILTER #( lt_vendas
USING KEY by_pais
WHERE pais = 'PT'
).
REDUCE (Agregações)
" Somar valores
DATA(lv_total) = REDUCE decfloat34(
INIT sum = 0
FOR ls_venda IN lt_vendas
NEXT sum = sum + ls_venda-valor
).
" Contar com condição
DATA(lv_count_ativos) = REDUCE i(
INIT cnt = 0
FOR ls_cliente IN lt_clientes
WHERE ( ativo = abap_true )
NEXT cnt = cnt + 1
).
" Encontrar máximo
DATA(lv_max_preco) = REDUCE decfloat34(
INIT max = 0
FOR ls_produto IN lt_produtos
NEXT max = COND #( WHEN ls_produto-preco > max
THEN ls_produto-preco
ELSE max )
).
" Concatenar strings
DATA(lv_nomes) = REDUCE string(
INIT texto = ''
FOR ls_cliente IN lt_clientes
NEXT texto = texto && ls_cliente-nome && ', '
).
CORRESPONDING
" Converter entre tabelas de tipos diferentes
DATA(lt_destino) = CORRESPONDING ty_t_destino( lt_origem ).
" Com BASE (manter existentes)
lt_destino = CORRESPONDING #( BASE lt_destino lt_origem ).
" Com MAPPING
DATA(lt_resumo) = CORRESPONDING ty_t_resumo( lt_vendas
MAPPING id_resumo = id_venda
total = valor
).
" EXCEPT campos
DATA(lt_parcial) = CORRESPONDING ty_t_parcial( lt_completo
EXCEPT campo1 campo2
).
Operações com Conjuntos
Comparação de Tabelas
" Verificar se iguais
IF lt_tabela1 = lt_tabela2.
WRITE: / 'Tabelas idênticas'.
ENDIF.
" Verificar se vazia
IF lt_clientes IS INITIAL.
WRITE: / 'Sem clientes'.
ENDIF.
IF lt_clientes IS NOT INITIAL.
" Processar
ENDIF.
Funções de Tabela
" Número de linhas
DATA(lv_count) = lines( lt_clientes ).
" Verificar existência
IF line_exists( lt_clientes[ id = 100 ] ).
WRITE: / 'Cliente existe'.
ENDIF.
" Obter índice
DATA(lv_index) = line_index( lt_clientes[ nome = 'João' ] ).
IF lv_index > 0.
WRITE: / |Encontrado no índice { lv_index }|.
ENDIF.
GROUP BY (Agrupamento)
" Agrupar e processar
LOOP AT lt_vendas INTO DATA(ls_venda)
GROUP BY ( pais = ls_venda-pais
ano = ls_venda-ano )
ASSIGNING FIELD-SYMBOL(<group>).
WRITE: / |País: { <group>-pais }, Ano: { <group>-ano }|.
" Processar membros do grupo
DATA(lv_total) = REDUCE decfloat34(
INIT sum = 0
FOR ls_item IN GROUP <group>
NEXT sum = sum + ls_item-valor
).
WRITE: / |Total: { lv_total }|.
ENDLOOP.
" Criar tabela agrupada
DATA(lt_agrupado) = VALUE ty_t_resumo(
FOR GROUPS <group> OF ls_venda IN lt_vendas
GROUP BY ( pais = ls_venda-pais )
( pais = <group>-pais
total = REDUCE decfloat34(
INIT sum = 0
FOR ls IN GROUP <group>
NEXT sum = sum + ls-valor
)
count = REDUCE i(
INIT cnt = 0
FOR ls IN GROUP <group>
NEXT cnt = cnt + 1
)
)
).
Tabelas Aninhadas
TYPES: BEGIN OF ty_cabecalho,
numero TYPE string,
data TYPE datum,
itens TYPE ty_t_itens,
END OF ty_cabecalho.
DATA(lt_pedidos) = VALUE ty_t_cabecalho(
( numero = 'P001'
data = sy-datum
itens = VALUE #(
( produto = 'A' qtd = 5 preco = '10.00' )
( produto = 'B' qtd = 3 preco = '20.00' )
)
)
( numero = 'P002'
data = sy-datum
itens = VALUE #(
( produto = 'C' qtd = 10 preco = '5.00' )
)
)
).
" Processar tabelas aninhadas
LOOP AT lt_pedidos INTO DATA(ls_pedido).
WRITE: / |Pedido: { ls_pedido-numero }|.
LOOP AT ls_pedido-itens INTO DATA(ls_item).
WRITE: /5 |Produto: { ls_item-produto }, Qtd: { ls_item-qtd }|.
ENDLOOP.
ENDLOOP.
Dicas de Boas Práticas
Escolha do Tipo de Tabela
- STANDARD: Uso geral, permite duplicados, acesso por índice
- SORTED: Quando precisa de ordem, busca binária automática
- HASHED: Lookups muito frequentes, grandes volumes
Performance
" ❌ Evitar - leitura linear em loop
LOOP AT lt_pedidos INTO DATA(ls_ped).
READ TABLE lt_clientes INTO DATA(ls_cli)
WITH KEY id = ls_ped-cliente_id.
ENDLOOP.
" ✅ Preferir - usar HASHED ou SORTED
DATA lt_clientes TYPE HASHED TABLE OF ty_cliente
WITH UNIQUE KEY id.
" ✅ Ou criar índice secundário
DATA lt_clientes TYPE STANDARD TABLE OF ty_cliente
WITH NON-UNIQUE KEY id
WITH UNIQUE HASHED KEY by_id COMPONENTS id.
Sintaxe Moderna
" ❌ Forma antiga
DATA lt_resultado TYPE ty_t_clientes.
DATA ls_cliente TYPE ty_cliente.
LOOP AT lt_clientes INTO ls_cliente WHERE ativo = abap_true.
APPEND ls_cliente TO lt_resultado.
ENDLOOP.
" ✅ Forma moderna
DATA(lt_resultado) = FILTER #( lt_clientes WHERE ativo = abap_true ).
Declaração de Chaves
" ❌ Evitar DEFAULT KEY (ambíguo)
DATA lt_data TYPE STANDARD TABLE OF ty_struct WITH DEFAULT KEY.
" ✅ Ser explícito
DATA lt_data TYPE STANDARD TABLE OF ty_struct WITH EMPTY KEY.
" ou
DATA lt_data TYPE STANDARD TABLE OF ty_struct
WITH NON-UNIQUE KEY campo1 campo2.
Tags: #Fundamentos #Tabelas-Internas #Modern-ABAP #Performance