macro SAS9

Exécuter des rapports conditionnels et éviter les pièges des macros

Simon 24/03/2022 1 views

L'automatisation est au cœur de la programmation SAS©. Un scénario classique consiste à vérifier le contenu d'une table avant de lancer un traitement : "Si ma table contient des données, lance le rapport standard, sinon lance un rapport d'anomalie."

Bien que la logique semble simple, sa mise en œuvre via le langage macro peut se heurter à deux obstacles majeurs : la portée des variables et le comportement trompeur de certaines variables automatiques comme SQLOBS.

Le Problème : Pourquoi mon code ne voit-il pas l'erreur ?

Imaginons un code structuré en deux macros :

  1. %Check_Data : Vérifie les données et crée un indicateur &error.

  2. %Run_Report : Lit &error et décide quel rapport imprimer.

L'erreur fréquente est de définir la variable &error à l'intérieur de la première macro sans précaution.

1. Le piège de la portée (Scope) Par défaut, une macro-variable créée à l'intérieur d'une macro (via %LET) est locale. Elle n'existe que le temps de l'exécution de cette macro. Dès que %Check_Data se termine, la variable &error est détruite. Lorsque %Run_Report tente de lire &error, elle ne trouve rien, et le programme plante (erreur "Apparent symbolic reference not resolved").

La Solution : Il faut déclarer la variable comme globale au début du programme ou avant son utilisation :

1%GLOBAL error;
2%LET error = 0;

Le Piège Technique : SQLOBS et les Vues

Une autre erreur subtile concerne la méthode de comptage. Le développeur tentait ici de détecter si une table était vide en vérifiant la variable automatique &SQLOBS juste après une instruction CREATE VIEW.

1PROC SQL;
2 CREATE VIEW work.ma_vue AS SELECT * FROM SOURCE;
3QUIT;
4/* Le développeur pense que &SQLOBS contient le nombre de lignes... */

C'est une erreur logique. La création d'une vue ne lit pas les données, elle ne fait que définir une structure. Par conséquent, SAS© renvoie toujours SQLOBS = 0 après la création d'une vue, même si la table sous-jacente contient des millions de lignes. Le rapport d'erreur se déclenchait donc systématiquement à tort.

La Bonne Pratique

Pour conditionner l'exécution de vos rapports (procédure PRINT, REPORT, etc.), voici la marche à suivre :

  1. Ne pas encapsuler vos appels de macros contenant des procédures (Proc Print, etc.) à l'intérieur d'une étape DATA _NULL_. Les macros s'appellent directement en code ouvert.

  2. Compter les lignes correctement : Pour vérifier si une table est vide sans la lire entièrement (ce qui est coûteux en ressources), interrogez les métadonnées via les dictionnaires SAS©.

Voici un exemple robuste :

1/* 1. Récupérer le nombre d'observations depuis les métadonnées */
2PROC SQL NOPRINT;
3 SELECT (nobs - delobs) INTO :num_obs
4 FROM dictionary.tables
5 WHERE LIBNAME = 'WORK' AND memname = 'MA_TABLE';
6QUIT;
7 
8/* 2. Logique conditionnelle */
9%MACRO Choisir_Rapport;
10 %IF &num_obs > 0 %THEN %DO;
11 %Put --- Lancement du Rapport Standard ---;
12 /* %Rapport_Standard; */
13 %END;
14 %ELSE %DO;
15 %Put --- TABLE vide : Lancement du Rapport d'Erreur ---;
16 /* %Rapport_Erreur; */
17 %END;
18%MEND;
19 
20%Choisir_Rapport;
Cette méthode est à la fois plus performante (pas de lecture de données inutile) et plus fiable pour gérer vos flux de production.
References & Docs