Langage SAS
Écrivez du code SAS idiomatique et de qualité production. Ce skill couvre le langage SAS lui-même, indépendamment de tout framework.
Portée
- Programmation DATA step (SET/MERGE/BY, RETAIN, tableaux, boucles DO, objets hash, traitement first./last.)
- PROC SQL (jointures, sous-requêtes, vues, passthrough avec CONNECT TO)
- Langage macro (%macro/%mend, variables macro, %local/%global, logique conditionnelle %IF, fonctions de quotation %STR/%NRSTR/%BQUOTE, CALL SYMPUTX)
- PROCédures courantes : SORT, MEANS/SUMMARY, FREQ, TRANSPOSE, REPORT, PRINT, CONTENTS, DATASETS, FORMAT, IMPORT/EXPORT
- Formats et informats (définis par l'utilisateur via PROC FORMAT, formats picture)
- Sortie ODS (HTML, PDF, RTF, Excel, destination OUTPUT)
- Entrées/Sorties fichier : LIBNAME, FILENAME, INFILE/INPUT, FILE/PUT
Workflow
Appliquez cette boucle à chaque demande qui produit du code SAS — nouveau programme, modification ou débogage :
- Analysez la demande. Identifiez les entrées : jeux de données, colonnes, variables macro, plateforme (SAS 9 vs Viya). Notez la sortie souhaitée : un jeu de données, un rapport (ODS), une variable macro, un fichier. Si la demande est ambiguë, énoncez votre hypothèse et poursuivez.
- Rédigez le code. Écrivez le programme minimal correct en suivant les règles de style ci-dessous. Utilisez des noms de jeux de données à deux niveaux, un
lengthexplicite pour les variables caractères,%localà l'intérieur des macros, etquit;pourproc sql. Préférezproc sort nodupkeyà une dé duplication manuelle. - Exécutez-le (ou décrivez l'exécution). Si une session SAS est disponible, soumettez et capturez le journal. Sinon, tracez le code mentalement par rapport à la liste des pièges courants — signalez toute étape que vous n'avez pas pu vérifier.
- Inspectez le journal. Considérez tout
WARNING:ouERROR:comme un défaut à corriger, pas une note à ignorer. Causes courantes : variables non initialisées, conversion implicite numérique→caractère, troncature due à une longueur implicite = 8, « aucune observation », références macro non résolues, variables macro automatiques en lecture seule. LesNOTEsuperflues doivent également être réduites lorsque c'est raisonnable. - Vérifiez par rapport aux pièges. Relisez le code final par rapport à la liste des pièges courants avant de le renvoyer. Corrigez ou annotez tout ce qui est signalé.
Entrées : une demande (langage naturel ou fichier .sas), éventuellement des données d'exemple ou un extrait de journal. Sorties : code SAS idiomatique (indentation à 2 espaces, terminé par run;/quit;) plus, si utile, une courte note sur tout piège traité ou hypothèse formulée. Vérification : le code renvoyé passe un scan mental du journal avec zéro WARNING/ERROR, et une exécution réelle (quand disponible) produit un journal propre.
Non négociable : aucun WARNING
Le code SAS généré doit s'exécuter proprement — zéro WARNING (et zéro ERROR) dans le journal. Traitez chaque WARNING: comme un défaut : variables non initialisées, conversions de type implicites, notes de troncature qui devraient être des avertissements, « aucune observation », références macro non résolues, etc. Si un avertissement est vraiment inévitable, supprimez-le délibérément (par exemple avec une option explicite) et commentez pourquoi. Évitez également les NOTES superflues lorsque c'est raisonnable.
Règles de style
Ces règles suivent les normes de codage @sasjs/core — appliquez-les à tout code SAS :
- Une instruction par ligne ; indentation = 2 espaces, pas de tabulations, pas d'espaces de fin de ligne
- Lignes ne dépassant pas 80 caractères ; fins de ligne Unix (LF) ; UTF-8
- Évitez complètement les caractères non-ASCII / spéciaux — compatibilité maximale entre les installations et codages SAS
- Terminez toujours les étapes par
run;; pourproc sql(et les procédures connectées à CAS),quit;est essentiel pour éviterWARNING: You cannot disconnect or terminate session ...sur Viya - Toutes les références à des jeux de données doivent être à deux niveaux (
work.blah, pasblah) — protège contreDATASTMTCHK=ALLKEYWORDSet une bibliothèqueUSERactive - Utilisez un
length/attribexplicite pour les variables caractères plutôt que de vous fier aux valeurs par défaut (évite la troncature implicite à longueur=8) - Utilisez des suffixes littéraux pour la clarté (
'01JAN2020'd,'12:30't) - Préférez
proc sortavecnodupkeyà la logique de dé duplication manuelle - Macros :
- Définissez avec des parenthèses, même sans paramètres :
%macro x();pas%macro x; - Le
%mend;de fermeture doit répéter le nom de la macro - Les appels de macro ne se terminent pas par un point-virgule :
%my_macro()pas%my_macro(); - Paramètres obligatoires positionnels ; paramètres optionnels de style mot-clé (
var=) - Noms de macro en minuscules, convention verbe-nom
- Variables macro sans point final (
&varpas&var.) sauf si nécessaire pour éviter une résolution incorrecte - Les NOMS des variables macro sont insensibles à la casse :
&Foo,&FOOet&foorésolvent tous au même symbole (idem pour les arguments%symexist/%superq/symget, qui prennent un NOM pas une valeur). Ne cherchez pas les différences de casse comme un bogue — ce n'est jamais la cause. - TOUTES les variables macro doivent être
%localsauf si délibérément globales (les globales doivent utiliser un préfixe d'application pour éviter les collisions) ; utilisezcall symputx(passymput) dans les DATA steps - Commentez avec
/* */à l'intérieur des macros (les commentaires astérisque sont compilés dans la macro) - Protégez la logique des macros avec des vérifications
%length(&var)=0plutôt que&var=(les comparaisons vides ne sont pas sûres)
- Définissez avec des parenthèses, même sans paramètres :
- Évitez les collisions de noms : utilisez des tables de travail basées sur
%sysfunc-/&syslast(par exempledata &output; set &syslast; run;) plutôt que des noms codés en dur - La bibliothèque WORK est automatiquement effacée à la fin de la session : SAS supprime toutes les tables de WORK lorsque la session se termine, donc un nettoyage explicite (
proc datasets lib=work nolist; delete ...; quit;) n'est pas requis pour les tables temporaires ordinaires — ne l'ajoutez pas, et ne signalez pas son absence comme un défaut lors de la révision. Nettoyez explicitement uniquement lorsque les fichiers temporaires sont volumineux ou nombreux au point de risquer une pression sur le disque en cours d'exécution ; dans ce cas, supprimez-les dès qu'ils ne sont plus nécessaires plutôt que de les laisser pour la fin de session. - Pas de code conditionnel ouvert (non macro) : enveloppez les blocs de branchement ou d'exécution conditionnelle de plateforme (par exemple
%if %mf_getplatform()=VIYA %then %do; ... %end;) dans une%macro ... %mendet invoquez la macro. Un%ifouvert au niveau du programme échoue dans certains contextes d'exécution (jobs/scheduler/bancs de test) et cache des fuites de portée.
Sensibilisation à la portabilité
- Notez quand le code diffère entre SAS 9.4 et Viya (par exemple actions CAS vs procédures,
proc casutilpour le chargement sashdat, pas de commande X sur les serveurs verrouillés) - Évitez les chemins physiques codés en dur et les options spécifiques au moteur sauf demande explicite
- Pas de code macro ouvert avec une logique if/else : enveloppez les blocs de branchement dans
%macro ... %mendet appelez-les — un%if/%elseouvert ne se comporte pas comme prévu dans tous les environnements SAS.
Exemples avant / après
Chaque paire montre un motif courant générateur de WARNING ou de défaut et la correction idiomatique, avec le problème de journal qu'il résout.
DATA step : variable non initialisée + troncature de longueur implicite
Avant — region est référencée mais jamais assignée ni importée via set, donc le journal montre WARNING: Variable region is uninitialized ; et name hérite de la longueur de first_name de la source (souvent $8), tronquant silencieusement la valeur concaténée :
data work.customers;
set work.source;
region = upcase(region); /* region pas dans work.source */
name = catx(' ', first_name, last_name); /* tronqué à la longueur source de name */
run;
Après — region est importée via set (ou assignée explicitement), et un length explicite avant set élargit name pour que catx ne soit pas tronqué :
data work.customers;
length name $ 50;
set work.source; /* region doit exister dans work.source */
region = upcase(region);
name = catx(' ', first_name, last_name);
run;
PROC SQL : conversion implicite + produit cartésien
Avant — la jointure sur id caractère vs cust_id numérique déclenche NOTE: Character values have been converted to numeric (ou un WARNING sous certaines options), et la clé de jointure manquante risque une explosion cartésienne :
proc sql;
create table work.joined as
select a.*, b.balance
from work.orders a, work.accounts b
where a.id = b.cust_id;
quit;
Après — length/cast explicite pour que les types correspondent, jointure interne explicite, et une protection contre le produit cartésien si les clés manquent :
proc sql;
create table work.joined as
select a.id, a.amount, b.balance
from work.orders as a
inner join work.accounts as b
on a.id = input(b.cust_id, 8.) /* cast explicite ; ou corriger la longueur en amont */
;
quit;
Si une colonne de clé a légitimement des doublons des deux côtés, agrégez ou dédoublonnez d'abord — ne comptez jamais sur une jointure 1:1 accidentelle.
Macro : %local manquant + vérification vide dangereuse
Avant — %let à l'intérieur de la macro crée dsid dans la portée globale (pas de %local), et %if &filter= est dangereux — quand &filter est vide, cela se résout en %if = %then, une erreur de syntaxe plutôt qu'une branche propre :
%macro apply_filter(filter=);
%if &filter= %then %do;
data work.out; set work.in; run;
%end;
%else %do;
data work.out; set work.in; where &filter; run;
%end;
%let dsid = &syslast;
%mend apply_filter;
Après — %local dsid garde la variable temporaire dans la portée de la macro, %length(&filter)=0 protège la vérification vide en toute sécurité, des parenthèses sur la définition de la macro, et %mend répète le nom :
%macro apply_filter(filter=);
%local dsid;
%if %length(&filter)=0 %then %do;
data work.out; set work.in; run;
%end;
%else %do;
data work.out; set work.in; where &filter; run;
%end;
%let dsid = &syslast;
%mend apply_filter;
Pièges courants à signaler lors de la révision
- Jointures MERGE plusieurs-à-plusieurs non intentionnelles
- Surprises de la variable automatique
_ERROR_/ RETAIN implicite - Problèmes de timing de macro : référence à
¯ovaravant son existence,%ifévaluant des variables DATA step (utilisezif/symgetà la place) - Les variables macro automatiques (
&syscc,&syswarningtext,&syserrortext,&sysdate,&sysuserid, etc.) sont EN LECTURE SEULE — tenter de les écraser (par exemple%let syswarningtext=;oucall symput('syscc',...)) génèreERROR: Unable to assign value to a macro variable that is read only(ou similaire). N'essayez jamais de les « effacer ». - Troncature due à une longueur implicite = 8 lors de la première affectation
- Avertissements de produit cartésien dans
proc sql
Limites
Ce skill est une référence de langage statique — il fournit des conseils pour écrire et réviser du code SAS. Il n'exécute pas de code SAS, n'exécute pas de commandes shell, n'accède pas au système de fichiers, ne se connecte pas à des bases de données et n'effectue pas de requêtes réseau. Tous les exemples de code sont illustratifs et doivent être soumis à une session SAS par l'utilisateur. Les termes comme « Entrées/Sorties fichier », « PROC SQL », « commande » et « exécution » font référence à des concepts du langage SAS (instructions de gestion de fichiers, procédure SQL, contextes d'exécution de macros), pas à des opérations au niveau du système d'exploitation effectuées par ce skill.