Linguagem SAS
Escreva código SAS idiomático e de produção. Este skill abrange a linguagem SAS em si, independente de qualquer framework.
Escopo
- Programação DATA Step (SET/MERGE/BY, RETAIN, arrays, loops DO, objetos hash, processamento first./last.)
- PROC SQL (joins, subconsultas, views, pass-through com CONNECT TO)
- Linguagem macro (%macro/%mend, variáveis macro, %local/%global, lógica condicional %IF, funções de quoting %STR/%NRSTR/%BQUOTE, CALL SYMPUTX)
- PROCs comuns: SORT, MEANS/SUMMARY, FREQ, TRANSPOSE, REPORT, PRINT, CONTENTS, DATASETS, FORMAT, IMPORT/EXPORT
- Formats e informats (definidos pelo usuário via PROC FORMAT, picture formats)
- Saída ODS (HTML, PDF, RTF, Excel, destino OUTPUT)
- E/S de arquivos: LIBNAME, FILENAME, INFILE/INPUT, FILE/PUT
Fluxo de trabalho
Aplique este loop a cada solicitação que produza código SAS — novo programa, edição ou depuração:
- Analise a solicitação. Identifique as entradas: datasets, colunas, variáveis macro, plataforma (SAS 9 vs Viya). Observe a saída desejada: um dataset, um relatório (ODS), uma variável macro, um arquivo. Se a solicitação for ambígua, declare sua suposição e prossiga.
- Elabore o código. Escreva o programa mínimo correto seguindo as regras de Estilo abaixo. Use nomes de dataset de 2 níveis,
lengthexplícito para variáveis caractere,%localdentro de macros equit;paraproc sql. Prefiraproc sort nodupkeyem vez de dedup manual. - Execute (ou descreva a execução). Se uma sessão SAS estiver disponível, submeta e capture o log. Caso contrário, percorra o código mentalmente contra a lista de Armadilhas comuns — aponte qualquer etapa que não pôde verificar.
- Inspecione o log. Trate qualquer
WARNING:ouERROR:como um defeito a ser corrigido, não uma nota a ser ignorada. Culpados comuns: variáveis não inicializadas, conversão numérico→caractere implícita, truncamento devido a length implícito=8, "no observations", referências de macro não resolvidas, variáveis macro automáticas somente leitura. NOTEs supérfluos também devem ser cortados quando razoável. - Revise contra armadilhas. Releia o código final contra a lista de Armadilhas comuns antes de retorná-lo. Corrija ou anote qualquer item sinalizado.
Entradas: uma solicitação (linguagem natural ou um arquivo .sas), opcionalmente dados de amostra ou um trecho de log. Saídas: código SAS idiomático (indentado com 2 espaços, terminado com run;/quit;) além de, quando útil, uma breve nota sobre qualquer armadilha abordada ou suposição feita. Verificação: o código retornado passa por uma varredura mental do log com zero WARNING/ERROR, e uma execução real (quando disponível) produz um log limpo.
Inegociável: sem WARNINGs
O código SAS gerado deve ser executado de forma limpa — zero WARNINGs (e zero ERRORs) no log. Trate cada WARNING: como um defeito: variáveis não inicializadas, conversões de tipo implícitas, notas de truncamento que deveriam ser warnings, "no observations", referências de macro não resolvidas, etc. Se um warning for verdadeiramente inevitável, suprima-o deliberadamente (ex.: uma opção explícita) e comente o porquê. Da mesma forma, evite NOTEs supérfluos quando razoável.
Regras de estilo
Estas seguem os padrões de codificação @sasjs/core — aplique-as a todo código SAS:
- Uma instrução por linha; indentação = 2 espaços, sem tabs, sem espaços em branco à direita
- Linhas com no máximo 80 caracteres; fim de linha unix (LF); UTF-8
- Evite caracteres não-ASCII / especiais completamente — máxima compatibilidade entre instalações e codificações SAS
- Sempre termine etapas com
run;; paraproc sql(e procs conectados ao CAS)quit;é essencial para evitarWARNING: You cannot disconnect or terminate session ...no Viya - Todas as referências a datasets devem ser de 2 níveis (
work.blah, nãoblah) — protege contraDATASTMTCHK=ALLKEYWORDSe uma bibliotecaUSERativa length/attribexplícito para variáveis caractere em vez de depender de padrões (evita truncamento por comprimento implícito=8)- Use sufixos literais para clareza (
'01JAN2020'd,'12:30't) - Prefira
proc sortcomnodupkeyem vez de lógica de dedup manual - Macros:
- Defina com parênteses, mesmo sem parâmetros:
%macro x();não%macro x; %mend;de fechamento deve repetir o nome da macro- Chamadas de macro não são terminadas com ponto e vírgula:
%my_macro()não%my_macro(); - Parâmetros obrigatórios posicionais; parâmetros opcionais no estilo palavra-chave (
var=) - Nomes de macro em minúsculas, convenção verbo-substantivo
- Variáveis macro sem ponto final (
&varnão&var.) a menos que necessário para evitar resolução incorreta - NOMES de variáveis macro não diferenciam maiúsculas de minúsculas:
&Foo,&FOOe&footodos resolvem para o mesmo símbolo (o mesmo para argumentos de%symexist/%superq/symget, que recebem um NOME, não um valor). Não persiga incompatibilidades de maiúsculas como um bug — nunca é a causa. - TODAS as variáveis macro devem ser
%locala menos que deliberadamente globais (globais devem usar um prefixo de aplicativo para evitar colisões); usecall symputx(nãosymput) em etapas DATA - Comente com
/* */dentro de macros (comentários com asterisco são compilados na macro) - Proteja a lógica macro com verificações
%length(&var)=0em vez de&var=(comparações vazias são inseguras)
- Defina com parênteses, mesmo sem parâmetros:
- Evite colisões de nomenclatura: use tabelas de trabalho baseadas em
%sysfunc-/&syslast(ex.:data &output; set &syslast; run;) em vez de nomes fixos - A biblioteca WORK é limpa automaticamente no término da sessão: o SAS exclui todas as tabelas no WORK quando a sessão termina, portanto, limpeza explícita (
proc datasets lib=work nolist; delete ...; quit;) não é necessária para tabelas temporárias comuns — não a adicione, e não sinalize sua ausência como um defeito na revisão. Limpe explicitamente apenas quando os arquivos temporários forem grandes ou volumosos o suficiente para arriscar pressão no disco durante a execução; nesse caso, descarte-os assim que não forem mais necessários, em vez de deixá-los para descarte no final da sessão. - Sem código condicional aberto (não-macro): envolva blocos de ramificação de plataforma ou execução condicional (ex.:
%if %mf_getplatform()=VIYA %then %do; ... %end;) em um%macro ... %mende invoque a macro.%ifaberto em nível de programa falha em alguns contextos de execução (jobs/schedulers/test harnesses) e esconde vazamentos de escopo.
Consciência de portabilidade
- Observe quando o código difere entre SAS 9.4 e Viya (ex.: ações CAS vs procs,
proc casutilpara carregamento sashdat, nenhum comando X em servidores bloqueados) - Evite caminhos físicos codificados e opções específicas de engine, a menos que solicitado
- Sem código macro aberto com lógica if/else: envolva blocos de ramificação em
%macro ... %mende chame-os —%if/%elseaberto não se comporta como esperado em todos os ambientes SAS.
Exemplos antes / depois
Cada par mostra um padrão comum propenso a WARNING ou defeito e a correção idiomática, com o problema de log que aborda.
DATA Step: variável não inicializada + truncamento por comprimento implícito
Antes — region é referenciada mas nunca atribuída ou trazida via set, então o log mostra WARNING: Variable region is uninitialized; e name herda o comprimento de first_name da fonte (frequentemente $8), truncando silenciosamente o valor concatenado:
data work.customers;
set work.source;
region = upcase(region); /* region não está em work.source */
name = catx(' ', first_name, last_name); /* truncado para o comprimento da fonte de name */
run;
Depois — region é trazida via set (ou atribuída explicitamente), e um length explícito antes de set aumenta name para que catx não seja truncado:
data work.customers;
length name $ 50;
set work.source; /* region deve existir em work.source */
region = upcase(region);
name = catx(' ', first_name, last_name);
run;
PROC SQL: conversão implícita + produto cartesiano
Antes — juntar em id caractere vs cust_id numérico dispara NOTE: Character values have been converted to numeric (ou um WARNING sob algumas opções), e a chave de junção ausente arrisca uma explosão cartesiana:
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;
Depois — length/cast explícito para que os tipos correspondam, inner join explícito e uma proteção contra o cartesiano se as chaves estiverem faltando:
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 explícito; ou corrija o comprimento upstream */
;
quit;
Se uma coluna de chave tiver duplicatas legítimas em ambos os lados, agregue ou dedupe primeiro — nunca confie em um join 1:1 acidental.
Macro: %local ausente + verificação de vazio insegura
Antes — %let dentro da macro cria dsid no escopo global (sem %local), e %if &filter= é inseguro — quando &filter está vazio, resolve para %if = %then, um erro de sintaxe em vez de um branch limpo:
%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;
Depois — %local dsid mantém a variável temporária no escopo da macro, %length(&filter)=0 protege a verificação de vazio com segurança, parênteses na definição da macro e %mend repete o nome:
%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;
Armadilhas comuns a sinalizar ao revisar
- MERGEs muitos-para-muitos não intencionais
- Surpresas com variável automática
_ERROR_/ RETAIN implícito - Problemas de temporização macro: referenciar
¯ovarantes de existir,%ifavaliando variáveis do data step (useif/symgetem vez disso) - Variáveis macro automáticas (
&syscc,&syswarningtext,&syserrortext,&sysdate,&sysuserid, etc.) são SOMENTE LEITURA — tentar sobrescrevê-las (ex.:%let syswarningtext=;oucall symput('syscc',...)) geraERROR: Unable to assign value to a macro variable that is read only(ou similar). Nunca tente "limpá-las". - Truncamento devido a comprimento implícito=8 na primeira atribuição
- Avisos de produto cartesiano em
proc sql
Limitações
Este skill é uma referência de linguagem estática — fornece orientação para escrever e revisar código SAS. Ele não executa código SAS, executa comandos shell, acessa o sistema de arquivos, conecta-se a bancos de dados ou faz requisições de rede. Todos os exemplos de código são ilustrativos e devem ser submetidos a uma sessão SAS pelo usuário. Termos como "E/S de arquivos", "PROC SQL", "comando" e "execução" referem-se a conceitos da linguagem SAS (instruções de manipulação de arquivos, procedimento SQL, contextos de execução de macro), não a operações de nível de sistema operacional realizadas por este skill.