Instruction ABAP READ TABLE : Trouver des lignes individuelles dans les tables internes

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

L’instruction READ TABLE sert à lire une seule ligne spécifique d’une table interne. Contrairement à LOOP AT, qui parcourt toutes ou plusieurs lignes, READ TABLE est optimisé pour l’accès ciblé à exactement une ligne – soit par la clé de table, un index ou une condition de recherche libre.

Syntaxe

Il existe différentes variantes pour identifier la ligne souhaitée :

1. Recherche via champs de clé (WITH KEY)

READ TABLE <table_interne>
INTO <zone_travail> | ASSIGNING <field_symbol> | REFERENCE INTO <data_reference>
WITH KEY <composante1> = <valeur1> [<composante2> = <valeur2> ...]
[BINARY SEARCH].

2. Recherche via clé de table (WITH TABLE KEY)

READ TABLE <table_interne>
INTO <zone_travail> | ASSIGNING <field_symbol> | REFERENCE INTO <data_reference>
WITH TABLE KEY <cle_composante1> = <valeur1> [<cle_composante2> = <valeur2> ...].

3. Accès via index (INDEX)

READ TABLE <table_interne>
INTO <zone_travail> | ASSIGNING <field_symbol> | REFERENCE INTO <data_reference>
INDEX <index>.

4. Recherche avec condition libre (WITH KEY ... COMPONENTS)

READ TABLE <table_interne>
INTO <zone_travail> | ASSIGNING <field_symbol> | REFERENCE INTO <data_reference>
WITH KEY <composante_table> COMPONENTS <composante1> = <valeur1> [...].

5. Vérification d’existence uniquement sans transport de données

READ TABLE <table_interne>
TRANSPORTING NO FIELDS
WITH KEY <composante> = <valeur>.

Composantes

  • <table_interne>: La table dans laquelle effectuer la recherche.
  • INTO <zone_travail>: La ligne trouvée est copiée dans la zone de travail.
  • ASSIGNING <field_symbol>: Le symbole de champ pointe directement sur la ligne trouvée.
  • REFERENCE INTO <data_reference>: Une référence de données sur la ligne trouvée.
  • WITH KEY: Recherche via composantes quelconques (pas forcément la clé de table).
  • WITH TABLE KEY: Recherche via la clé primaire définie de la table (optimisé).
  • INDEX <index>: Accès direct à la ligne à la position indiquée.
  • BINARY SEARCH: Recherche binaire (uniquement pour les tables standard triées – la table doit être triée !).
  • TRANSPORTING NO FIELDS: Aucune donnée n’est transportée, seuls sy-subrc et sy-tabix sont définis.

Champs système

Après un READ TABLE, les champs système suivants sont définis :

  • sy-subrc:

    • 0: Ligne trouvée.
    • 4: Aucune ligne correspondante trouvée.
    • 8: (Avec BINARY SEARCH) Ligne non trouvée, mais sy-tabix pointe sur la position d’insertion.
  • sy-tabix: Contient l’index de la ligne trouvée (pour les tables indexées). Pour les tables de hachage, sy-tabix n’est pas défini.

Comportement de recherche selon le type de table

Le comportement de recherche et la performance dépendent du type de table :

Type de tableWITH KEYWITH TABLE KEYINDEXPerformance
STANDARD TABLERecherche linéaire O(n)Recherche linéaire O(n)O(1)Lent pour grandes tables
SORTED TABLERecherche binaire O(log n)Recherche binaire O(log n)O(1)Rapide
HASHED TABLEHash-Lookup O(1)Hash-Lookup O(1)ImpossibleTrès rapide

Exemples

1. READ TABLE avec INTO (Copie)

TYPES: BEGIN OF ty_customer,
id TYPE i,
name TYPE string,
city TYPE string,
END OF ty_customer.
DATA: lt_customers TYPE STANDARD TABLE OF ty_customer WITH EMPTY KEY,
ls_customer TYPE ty_customer.
" Remplir la table
lt_customers = VALUE #(
( id = 1 name = 'Müller GmbH' city = 'Berlin' )
( id = 2 name = 'Schmidt AG' city = 'München' )
( id = 3 name = 'Weber KG' city = 'Hamburg' )
).
" Rechercher le client avec ID 2
READ TABLE lt_customers INTO ls_customer WITH KEY id = 2.
IF sy-subrc = 0.
WRITE: / 'Trouvé:', ls_customer-name, 'à', ls_customer-city.
WRITE: / 'Index:', sy-tabix.
ELSE.
WRITE: / 'Client non trouvé.'.
ENDIF.

2. READ TABLE avec ASSIGNING (Accès direct)

FIELD-SYMBOLS: <fs_customer> TYPE ty_customer.
" Rechercher le client et modifier directement
READ TABLE lt_customers ASSIGNING <fs_customer> WITH KEY id = 1.
IF sy-subrc = 0.
" La modification agit directement dans la table
<fs_customer>-city = 'Frankfurt'.
WRITE: / 'Ville modifiée en:', <fs_customer>-city.
ELSE.
WRITE: / 'Client non trouvé.'.
ENDIF.

3. READ TABLE avec INDEX

" Lire la première ligne
READ TABLE lt_customers INTO ls_customer INDEX 1.
IF sy-subrc = 0.
WRITE: / 'Première ligne:', ls_customer-name.
ENDIF.
" Lire la dernière ligne
READ TABLE lt_customers INTO ls_customer INDEX lines( lt_customers ).
IF sy-subrc = 0.
WRITE: / 'Dernière ligne:', ls_customer-name.
ENDIF.
" IMPORTANT: La table doit être triée selon le champ de recherche !
" Voir: /sort-statement/
SORT lt_customers BY id.
READ TABLE lt_customers INTO ls_customer
WITH KEY id = 2
BINARY SEARCH.
IF sy-subrc = 0.
WRITE: / 'Trouvé (binaire):', ls_customer-name.
ENDIF.

Attention: BINARY SEARCH sur une table non triée fournit des résultats incorrects sans message d’erreur !

5. READ TABLE avec TRANSPORTING NO FIELDS

" Vérifier uniquement si un client existe
READ TABLE lt_customers TRANSPORTING NO FIELDS
WITH KEY city = 'Berlin'.
IF sy-subrc = 0.
WRITE: / 'Au moins un client à Berlin (Index:', sy-tabix, ').'.
ELSE.
WRITE: / 'Aucun client à Berlin.'.
ENDIF.

6. READ TABLE avec plusieurs champs de clé

TYPES: BEGIN OF ty_order,
customer_id TYPE i,
order_id TYPE i,
amount TYPE p DECIMALS 2,
END OF ty_order.
DATA: lt_orders TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY,
ls_order TYPE ty_order.
lt_orders = VALUE #(
( customer_id = 1 order_id = 100 amount = '1500.00' )
( customer_id = 1 order_id = 101 amount = '2300.50' )
( customer_id = 2 order_id = 100 amount = '800.00' )
).
" Rechercher la commande pour le client 1, commande 101
READ TABLE lt_orders INTO ls_order
WITH KEY customer_id = 1
order_id = 101.
IF sy-subrc = 0.
WRITE: / 'Montant:', ls_order-amount.
ENDIF.

7. READ TABLE avec REFERENCE INTO

DATA: lr_customer TYPE REF TO ty_customer.
READ TABLE lt_customers REFERENCE INTO lr_customer WITH KEY id = 3.
IF sy-subrc = 0.
" Accès via déréférencement
WRITE: / 'Nom:', lr_customer->name.
lr_customer->city = 'Köln'. " Modifie directement la table
ENDIF.

Alternative moderne : Expressions de table (à partir d’ABAP 7.40)

À partir d’ABAP 7.40, les expressions de table peuvent être utilisées comme alternative compacte :

Accès en ligne avec expression de table

" Accès direct (lève une exception si non trouvé)
TRY.
DATA(ls_found) = lt_customers[ id = 2 ].
WRITE: / 'Trouvé:', ls_found-name.
CATCH cx_sy_itab_line_not_found.
WRITE: / 'Non trouvé.'.
ENDTRY.
" Accès par index
DATA(ls_first) = lt_customers[ 1 ].
" Avec OPTIONAL (retourne une ligne initiale si non trouvé)
DATA(ls_safe) = VALUE #( lt_customers[ id = 99 ] OPTIONAL ).
" Avec DEFAULT (retourne une valeur par défaut si non trouvé)
DATA(ls_default) = VALUE #( lt_customers[ id = 99 ]
DEFAULT VALUE #( id = 0 name = 'Inconnu' ) ).

Vérification d’existence avec line_exists()

IF line_exists( lt_customers[ id = 2 ] ).
WRITE: / 'Le client 2 existe.'.
ENDIF.
IF NOT line_exists( lt_customers[ city = 'Stuttgart' ] ).
WRITE: / 'Aucun client à Stuttgart.'.
ENDIF.

Déterminer l’index avec line_index()

DATA(lv_index) = line_index( lt_customers[ id = 2 ] ).
IF lv_index > 0.
WRITE: / 'Le client 2 est à la position:', lv_index.
ELSE.
WRITE: / 'Non trouvé.'.
ENDIF.

READ TABLE vs. Expression de table

AspectREAD TABLEExpression de table
SyntaxePlus détailléeCompacte
Non trouvésy-subrc = 4Exception ou OPTIONAL
Index disponiblesy-tabixline_index()
PerformanceIdentiqueIdentique
ASSIGNINGOuiOui (avec FIELD-SYMBOL)
" Expression de table avec symbole de champ
ASSIGN lt_customers[ id = 2 ] TO FIELD-SYMBOL(<fs_cust>).
IF sy-subrc = 0.
<fs_cust>-city = 'Dresden'.
ENDIF.

Différence avec LOOP AT

  • READ TABLE: Lit une seule ligne. Idéal pour les accès ciblés.
  • LOOP AT: Parcourt plusieurs/toutes les lignes séquentiellement.
" Lire une ligne spécifique → READ TABLE
READ TABLE lt_customers INTO ls_customer WITH KEY id = 2.
" Traiter toutes les lignes avec un critère spécifique → LOOP AT
LOOP AT lt_customers INTO ls_customer WHERE city = 'Berlin'.
" Traiter plusieurs lignes
ENDLOOP.

Conseils de performance

  1. Choisir le bon type de table :

    • Accès fréquents par clé → SORTED TABLE ou HASHED TABLE
    • Accès séquentiel → STANDARD TABLE
  2. BINARY SEARCH uniquement pour les tables triées : Utilisez SORT avant la recherche binaire :

    " Incorrect: BINARY SEARCH sans tri
    READ TABLE lt_unsorted INTO ls_wa WITH KEY field = value BINARY SEARCH. " Dangereux !
    " Correct: Trier d'abord
    SORT lt_data BY field.
    READ TABLE lt_data INTO ls_wa WITH KEY field = value BINARY SEARCH.
  3. Utiliser les clés secondaires :

    DATA: lt_customers TYPE SORTED TABLE OF ty_customer
    WITH UNIQUE KEY id
    WITH NON-UNIQUE SORTED KEY by_city COMPONENTS city.
    " Accès rapide via clé secondaire
    READ TABLE lt_customers INTO ls_customer
    WITH KEY by_city COMPONENTS city = 'Berlin'.
  4. TRANSPORTING NO FIELDS pour les vérifications d’existence : Si on veut seulement vérifier qu’une ligne existe, TRANSPORTING NO FIELDS est plus efficace.

  5. Expressions de table avec line_exists() : Pour les vérifications d’existence pures, line_exists() est élégant et performant.

Remarques importantes / Bonnes pratiques

  • Vérifiez toujours sy-subrc après un READ TABLE avant d’accéder aux données résultantes.
  • Utilisez ASSIGNING pour une meilleure performance et si vous souhaitez modifier la ligne. Alternativement, vous pouvez utiliser MODIFY.
  • Utilisez BINARY SEARCH uniquement si la table est garantie triée (voir SORT) – sinon les résultats sont peu fiables.
  • Pour les accès fréquents par clé, envisagez des tables triées ou de hachage.
  • À partir d’ABAP 7.40, les expressions de table sont souvent l’alternative plus élégante et plus courte.
  • Pour les tables de hachage, sy-tabix après READ TABLE n’est pas défini – ne vous y fiez pas.
  • READ TABLE est pour les tables internes. Pour les tables de base de données, utilisez SELECT SINGLE.