Posts Tagged programme

Caractères interdits sur BW

Je vous conseille d’aller lire cet article à sa nouvelle adresse : Caractères interdits sur BW

Lors de chargements il peut arriver d’avoir une erreur à cause de caractères interdits : “Value ‘xxxxx’ for characteristic YYYY contains invalid characters

La transaction RSKC permet de définir la liste des caractères autorisés. Classiquement le générique “ALL_CAPITAL” est utilisé. Mais à quoi correspond-il exactement ?

ALL_CAPITAL autorise tous les caractères à l’exception de 31 caractères de contrôle, c’est-à-dire des caractères invisibles généralement utilisés par les programmes de traitement de texte pour gérer la mise en forme. Aucun de ces caractères ne peut être saisi directement dans SAP, seules des interfaces tiers peuvent éventuellement les injecter. En revanche, il est fréquent de les introduire par erreur lors de copier/coller depuis des logiciels tiers (Word, Excel ou Outlook par exemple).

SAP ne sachant pas les interpréter, ils sont affichés comme des # (mais sont conservés dans leur forme d’origine grâce à Unicode).

Voici la liste des 31 caractères interdits par ALL_CAPITAL :

Code Désignation
01 START OF HEADING
02 START OF TEXT
03 END OF TEXT
04 END OF TRANSMISSION
05 ENQUIRY
06 ACKNOWLEDGE
07 BELL
08 BACKSPACE
09 CHARACTER TABULATION
0A LINE FEED (LF)
0B LINE TABULATION
0C FORM FEED (FF)
0D CARRIAGE RETURN (CR)
0E SHIFT OUT
0F SHIFT IN
10 DATA LINK ESCAPE
11 DEVICE CONTROL ONE
12 DEVICE CONTROL TWO
13 DEVICE CONTROL THREE
14 DEVICE CONTROL FOUR
15 NEGATIVE ACKNOWLEDGE
16 SYNCHRONOUS IDLE
17 END OF TRANSMISSION BLOCK
18 CANCEL
19 END OF MEDIUM
1A SUBSTITUTE
1B ESCAPE
1C INFORMATION SEPARATOR FOUR
1D INFORMATION SEPARATOR THREE
1E INFORMATION SEPARATOR TWO
1F INFORMATION SEPARATOR ONE

Pour autoriser ces caractères, il est possible d’utiliser “ALL_CAPITAL_PLUS_HEX” dans RSKC.

Quelques fonctions utiles :
RSKC_ALLOWED_CHAR_GET permet d’obtenir la liste des caractères autorisés saisis dans RSKC.
RSKC_CHAVL_CHECK permet de tester si une valeur va être autorisée par BW.

Un petit fragment de code qui permet de supprimer d’une variable les 31 caractères :

* w_data contient le texte à nettoyer
  DATA: l_length TYPE i,
        l_pos TYPE i,
        lo_not_allowed TYPE REF TO cl_abap_conv_in_ce,
        l_not_allowed_x(32) TYPE x VALUE
'000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F',
        l_not_allowed TYPE string.

* Get list of unauthorized values
  lo_not_allowed = cl_abap_conv_in_ce=>create( input = l_not_allowed_x ).
  lo_not_allowed->read( IMPORTING data = l_not_allowed ).

* search if unauthorized value is found
  IF w_data CA l_not_allowed.
* If found, replace it by space
    l_length = STRLEN( w_data ).
    l_pos = 0.
    DO l_length TIMES.
      IF w_data+l_pos(1) CA l_not_allowed.
        w_data+l_pos(1) = space.
      ENDIF.
      l_pos = l_pos + 1.
    ENDDO.
  ENDIF.

Tags: , , ,

Migration BW3 – BI7 : vous reprendrez bien un peu de hash ?

Je vous conseille d’aller lire cet article à sa nouvelle adresse : Migration BW3 – BI7 : vous reprendrez bien un peu de hash ?

Ce matin, j’étais tranquillement en train de m’occuper de ma migration, quand tout à coup l’un de mes chargements dump. Je suis interloqué car le dump apparait lorsque je lance l’infopackage, sur le mode actif. Pas en background comme cela arrive habituellement.

En-tête du DUMP

En-tête du DUMP

Log du dump :

Category : ABAP Server Resource Shortage
Runtime Errors : TSV_TNEW_PAGE_ALLOC_FAILED

What happened?
You attempted to extend an internal table, but the required space was not available.

Information on where terminated
Termination occurred in the ABAP program "SAPLRSSM_LOAD" - in "RSSM_RSSELDONE_READ".  The main program was "RSAWBN_START ".  In the source code you have the termination point in line 245 of the (Include) program "LRSSM_LOADU08".

Allant voir plus loin dans le log je me rend compte que le dump survient sur un select avec des conditions dynamiques

Fragment de code en cause pour le DUMP

Fragment de code en cause pour le DUMP

Me voici donc partit pour débugger le tout. J’arrive rapidement à trouver la valeur des paramètres et après un petit tour dans SE16, je suis catastrophé. 6.8 millions d’entrées correspondent à cette sélection. Voici donc pourquoi le chargement dump. Ca fait un peu beaucoup de lignes à mettre dans une table interne…

En revanche j’ai du mal à m’expliquer pourquoi SAP fait ce chargement. La table RSSELDONE contient les paramètres de chargement de toutes les requests. Quel intérêt d’aller charger en mémoire tout l’historique des paramètres de sélection sur ma source ???

Après arrachage de poignées de cheveux et traitage de noms d’oiseaux des développeurs de Waldorf, j’ai fini par comprendre. SAP a ajouté un hash code dans cette table. Il n’était pas présent sur la version 3.x et se trouve dans la 7.x. Dès qu’on fait un chargement en BI7.x, le hash code correspondant est généré et stocké dans RSSELDONE. Mais quid des entrées 3.x lors d’une migration ? Et bien SAP a décidé que le PREMIER CHARGEMENT serait le parfait moment pour générer LA TOTALITE des hash code de chargement de la source correspondante !

Bon c’est bien, je sais maintenant pourquoi ca dump, mais ça ne m’aide pas à corriger. J’ai fini par trouver un programme standard qui permet de générer les hash code : RSSM_HASH_ENTRIES_CREATE. Malheureusement il a probablement été écrit par un stagiaire… son temps d’exécution est très très très (trop) long. Aussi, pour les besoins du projet, j’ai du faire une version light qui appelle directement la fonction standard de génération de hash, sans appeler des tonnes de fonctions inutiles autour…

2h plus tard, j’ai pu relancer mon infopackage qui a chargé sans problème mes données. Affaire classée !

Au cas où cette mésaventure vous arrive, voici le programme dont je me suis servi.

Télécharger le programme correctif

Tags: , , , ,

Cure de rajeunissement pour AL11

Je vous conseille d’aller lire cet article à sa nouvelle adresse : Cure de rajeunissement pour AL11

Le travail sur un datawarehouse nous confronte régulièrement à des injections de fichier. Et sur SAP, nous ne sommes pas gâté niveau outils…
J’entendais souvent mes collègues râler à propos de la transaction AL11, la seule chose dont nous disposons en standard pour naviguer sur le serveur SAP.

J’ai parcouru la toile à la recherche d’une solution de remplacement mais ne fut pas convaincu par ce que j’y ai trouvé, aussi j’ai pris mon courage (et mon clavier) à deux mains afin de réaliser ma propre solution : plus complète, plus lisible et avec l’ergonomie qui me sied (mais je ne crache pas dans leur soupe, j’ai d’ailleurs repris leur manière de déterminer l’icone relatif a chaque type de fichier).

ZAL11 display local and server file explorer

ZAL11 display local and server file explorer

Entièrement graphique, le programme “ZAL11″ (tout simplement ;-) ) vous permettra de naviguer sur le serveur SAP, mais également :

  • D’ouvrir les fichiers avec votre application par défaut sur votre PC (excel pour les CSV par exemple)
  • D’ouvrir les fichiers avec un éditeur de texte sur votre PC
  • De télécharger ou télédécharger des fichiers
  • De gérer les fichiers/répertoires du serveur (ajout, copie, déplacement, renommage)
  • De compresser (tar+bz2) ou décompresser (zip, tar, gz, bz2) des fichiers et répertoires sur le serveur
  • De copier dans le presse-papier le chemin d’un fichier sur le serveur, ou d’accéder directement au répertoire d’un chemin contenu dans le presse-papier.

La gestion des raccourcis serveur de AL11 est conservée (et débuggée ;-) )
Vu qu’il s’agit de manipulation sur le serveur, chaque action demande une confirmation via un popup qui récapitule ce qui est en train d’être fait.

ZAL11 File and folder context menu

ZAL11 File and folder context menu

Le code a été écrit dans une optique de sécurité, de maintenabilité et de respect des normes de développement usuels. Il est entièrement documenté en anglais (25% de commentaires ajoutés par rapport au code brut).
La principale “entorse” aux règles est que le code est fourni dans un seul pavé de 3500 lignes au lieu d’être découpé en includes (top, class, pbo, pai, form). C’est volontaire afin de n’avoir qu’un seul code source à fournir. Néanmoins chaque partie est clairement délimitée et il ne devrait pas falloir plus d’une minute à un développeur pour recréer les includes nécessaires. Pour la même raison de simplicité de partage, aucune classe de message n’a été créée.

Télécharger ZAL11 (compatible ECC) (Si vous n’arrivez pas à télécharger le fichier : click droit puis enregistrer la cible sous… et n’oubliez pas que vous aurez besoin de SAPLINK pour l’installer ensuite)

Tags: , , , , , ,

ZTOAD – Requêteur Open SQL – mise à jour

Je vous conseille d’aller lire cet article à sa nouvelle adresse : ZTOAD – Requêteur Open SQL – mise à jour

Petite mise a jour de mon requêteur open SQL, nommé ZTOAD :
- Correction d’un bug lorsque le nom des champs était trop long
- Affichage du nom technique du champs dans l’entête de colonne ALV si aucun libellé n’est trouvé. (ca évite les colonnes sans libellé)
- Ajout d’une mesure du temps d’exécution de la requête (affiché dans l’entête)

Télécharger ZTOAD (compatible 4.7+)

La présentation complète se trouve ici

Tags: , , , , ,

Performances et boucles imbriquées

Je vous conseille d’aller lire cet article à sa nouvelle adresse : Performances et boucles imbriquées

Je suis régulièrement sollicité pour résoudre des problèmes de performance sur des reports ou des interfaces.
En premier lieu, j’analyse les requêtes SQL, puis je vérifie la manière dont l’algorithme de traitement des données est implémenté.

Parmi les erreurs les plus classiques, on retrouve la gestion des boucles imbriquées. Prenons un exemple.
Nous voulons lister des données imputation de commande d’achat.
Une première table interne t_ekko contient les données d’en-tête (1000 commandes). Une seconde t_epko contient les données de poste (10 postes par commande = 10 000 postes). Une 3e t_ekkn contient les données imputations (2 imputations par poste = 20 000 imputations).
La manière simple (et anti performante) d’écrire le traitement serait :

LOOP AT t_ekko.
  LOOP AT t_ekpo WHERE ebeln = t_ekko-ebeln.
    LOOP AT t_ekkn WHERE ebeln = t_ekko-ebeln AND ebelp = t_ekpo-ebelp.
*     traitement imputation
    ENDLOOP.
*   traitement poste
  ENDLOOP.
* traitement commande
ENDLOOP.

Si cette syntaxe est rapide à écrire et simple à relire, elle présente l’inconvénient majeur que le LOOP AT txx WHERE fait un “full scan” de la table interne. Chaque ligne sera lue par le moteur pour voir si elle correspond aux critères WHERE.
Ainsi pour un report sur 1000 commandes, ayant chacune 10 postes avec 2 imputations, la table t_ekkn (20 000 entrées) est parcourue 10 000 fois, soit 200 millions de lignes lues !

Pour ce genre de tables, la lecture peut etre synchronisée entre les 3 tables via l’utilisation d’index

DATA : l_index_ekpo TYPE i,
       l_index_ekkn TYPE i.

SORT t_ekko BY ebeln.
SORT t_ekpo BY ebeln ebelp.
SORT t_ekkn BY ebeln ebelp.

LOOP AT t_ekko.
  LOOP AT t_ekpo FROM l_index_ekpo.
    IF t_ekpo-ebeln > t_ekko-ebeln.
      l_index_ekpo = sy-index.
      EXIT.
    ELSEIF t_ekpo-ebeln < t_ekko-ebeln.       CONTINUE.     ELSE.       LOOP AT t_ekkn FROM l_index_ekkn.         IF t_ekkn-ebeln > t_ekko-ebeln
        OR ( t_ekkn-ebeln = t_ekko-ebeln AND t_ekkn-ebelp > t_ekpo-ebelp ).
          l_index_ekpo = sy-index.
          EXIT.
        ELSEIF t_ekpo-ebeln < t_ekko-ebeln
        OR ( t_ekkn-ebeln = t_ekko-ebeln AND t_ekkn-ebelp < t_ekpo-ebelp ).
          CONTINUE.
        ELSE.
*         traitement imputation
        ENDIF.
      ENDLOOP.
*     traitement poste
    ENDIF.
  ENDLOOP.
* traitement commande
ENDLOOP.

Grâce aux index, chacune des 3 tables n’est parcourue qu’une seule fois, minimisant ainsi au maximum le travail de lecture du moteur ABAP. (soit 30 000 lignes lues pour t_ekkn [Les 2 lignes d'imputations + la première ligne d'imputation du poste suivant])
Néanmoins cette solution présente l’inconvénient d’être assez indigeste (peu lisible) et peut facilement introduire des bugs si elle est mal maitrisée. De plus, il faut que les 3 tables soient triées selon le même axe, ce qui n’est pas forcément possible dans tous les cas.

Aussi il existe une méthode intermédiaire, largement utilisée par SAP. Il s’agit de faire une recherche de la première ligne répondants aux critères, puis une lecture directe par index.

DATA : l_index_ekpo TYPE i,
       l_index_ekkn TYPE i.

SORT t_ekpo BY ebeln.
SORT t_ekkn BY ebeln ebelp.

LOOP AT t_ekko.
  READ TABLE t_ekpo WITH KEY ebeln = t_ekko-ebeln
                    BINARY SEARCH
                    TRANSPORTING NO FIELDS.
  IF sy-subrc = 0.
    l_index_ekpo = sy-tabix.
    LOOP AT t_ekpo FROM l_index_ekpo.
      IF t_ekpo-ebeln NE t_ekko-ebeln.
        EXIT.
      ENDIF.
      READ TABLE t_ekkn WITH KEY ebeln = t_ekko-ebeln
                                 ebelp = t_ekpo-ebelp
                        BINARY SEARCH
                        TRANSPORTING NO FIELDS.
      IF sy-subrc = 0.
        l_index_ekkn = sy-tabix.
        LOOP AT t_ekkn FROM l_index_ekkn.
          IF t_ekkn-ebeln NE t_ekko-ebeln OR t_ekkn-ebelp NE t_ekpo-ebelp.
            EXIT.
          ENDIF.
*         traitement imputation
        ENDLOOP.
      ENDIF.
*     traitement poste
    ENDLOOP.
  ENDIF.
* traitement commande
ENDLOOP.

Un peu moins performante que la syntaxe précédente, cette manière est néanmoins un peu plus simple à écrire (et à relire/comprendre).
Le read table binary search est très performant (recherche dichotomique). Pour t_ekkn contenant 20 000 entrées, seulement 14 lignes sont lues pour trouver la bonne entrée. Le moteur va donc lire 170 000 lignes sur t_ekkn au total (14 pour trouver l’imputation + 2 imputations + la première imputation du poste suivant).

Méthode Lignes t_ekkn parcourues Complexité
Loop at … where 200 000 000 Simple
Loop from index 30 000 Complexe
Read table + Loop from index 170 000 Moyenne

Tags: , ,