SAS Guide: Ejecutar código al conectarse

En ocasiones utilizamos repetidas veces las mismas definiciones de librerías que no están predeterminadas por el administrador en el sistema o tenemos que cargar ciertas macros que utilizamos repetidamente en nuestros distintos programas. Este trabajo de repetir estos trozos de código a veces puede resultar pesado e incluso comerse parte de nuestro preciado tiempo.

Una forma para realizar esta tarea de forma automática cada vez que se inicia SAS Guide es utilizando el autoexec. Este es un fichero que contiene un trozo de código que se ejecutará al inicio de la sesión y que es exclusivo de cada usuario, por lo que podemos personalizarlo a nuestro gusto.

Podemos editar este autoexec desde las opciones de SAS Guide. Para ello, primero crearemos el programa que contiene el código que necesitamos ejecutar y lo guardaremos preferiblemente en una ruta de red donde otros usuarios puedan alcanzarlo para que puedan utilizar nuestro código. No es nada recomendable guardarlo en una carpeta personal si se trabaja en un equipo.

Una vez hecho esto, en el menú de SAS Guide vamos al menú Herraminetas > Opciones > Programas SAS y hacemos check en la opción «Procesar el código SAS cuando el servidor esté conectado». Esto ejecutará el siguiente código cuando se realice una conexión al workspace. De esta forma, las librerías o macros que creemos estarán disponibles nada más abrir SAS y podrán ser llamadas desde cualquier programa que ejecutemos nosotros.

Menú de Opciones de SAS Guide

Para editar el código que se ejecutará en ese momento haremos clic en el botón Editar. Aparecerá una ventana de código donde incluiremos la llamada al código que nos interese. En este caso recomiendo incluir un include que llame al programa SAS que ya habíamos generado. Para ello, escribimos:

%include «[ruta]\[nombre_programa.sas]»;

Donde ruta es la ruta completa de la carpeta donde se encuentra el código y nombre_programa.sas el nombre del programa que se ha creado.

Cualquier otro usuario que ejecute un programa que contenga estas librerías o macros que hemos definido aquí; o en caso de planificar un programa, no los tendrán disponibles por defecto, ya que este cambio solo afecta a tu propia sesión. Pero esto se arregla fácilmente incluyendo la misma línea del %include que antes hemos visto al principio de ese programa y todo funcionará correctamente.

Macro SAS: Validar teléfonos (España)

Esta macro que incluyo a continuación permite validar número de teléfonos de España, aunque no se encuentre normalizados, esto es, contengan el prefijo internacional o no, o estén separados por algún tipo de separador o de espacio. Al final se ofrecen los números de teléfonos normalizados y sin prefijo.

Esta macro solo hace una verificación del formato del número del teléfono, pero no sabe distinguir entre números reales o no. Para hacer este tipo de verificaciones tenemos servicios que dan información vía API (protocolo de comunicación entre sistemas) devolviendo un fichero JSON sobre el tipo de teléfono, operador, país, etc. Espero poder poner un ejemplo sobre cómo integrar un servicio de terceros como este en un futuro (Hazme un comentario si estás interesado en ello).

%macro telefono(tabla=,campo=);
data &tabla;
set &tabla;
&campo = compress(&campo,' ','P');
if substr(&campo,1,4) = '0034' then &campo = substr(&campo,5);
if substr(&campo,1,2) = '34' then &campo = substr(&campo,3);
if substr(&campo,1,1) not in ('6','7','8','9') or
substr(&campo,1,2) in ('80','90') or
length(&campo) ne 9 then ind_valido = 0;
else ind_valido = 1;
run;
%mend;

Puedes probar con estos ejemplos la macro:

data clientes;
    format telefono $20.;
    input telefono;
    datalines;
    9851246323
    981122044
    +34666777888
    902123123
    0034-616875794
    +0034 615 789 456
    658.452.187
    001 32157979
    34715845978
    888123456
    (95)6543232
    ;
run;*/

%telefono(tabla=clientes,campo=telefono);

Macro SAS: Algoritmo para validar el código de cuenta bancaria (IBAN)

Aquí tenemos una macro que sirve para validar el código de cuenta bancario, IBAN, que rige en muchos países de Europa, Caribe y Medio Oriente. Se le pasan como parámetros el nombre de la tabla y el nombre del campo que contiene el IBAN en formato electrónico (todos seguido, sin espacios) y devuelve un indicador de si es válido o no y el nomobre de la entidad bancaria a la que pertenece.

El código IBAN tiene tres partes, los dos primeros dígitos corresponden al país, los dos segundos son el código de verificación que se calculan en función de todos los demás y que validan que el código se ha escrito bien; y una última parte que es variable. Cada país define la estructura de esta tercera parte que es un código alfanumérico de hasta 30 caracteres, ya que el código IBAN tiene un máximo de 34 caracteres totales.

En el caso de España, esta tercera parte del IBAN corresponde a 4 dígitos para el código de la entidad bancaria tal y como se lo asigna el Banco de España, otros 4 dígitos para el código de la oficina bancaria, los dos siguientes corresponden al código de verificación del antiguo CCC (el antiguo número de cuenta que teníamos en España), y finalmente 10 caracteres para el número de cuenta bancaria. El total de caracteres para España son 24.

Esta macro valida el código del país, que tiene que estar incluido en la lista de países adjunta a este post, el código de oficina bancaria para los IBAN españoles (también según listado adjunto). Será necesario crear las tabla SAS PAISES y BANCOS respectivamente para que la macro funcione plenamente. Lamentablemente, solo valida códigos IBAN españoles.

%macro normalizar_ccc(tabla=,campo=);
    /* Identificar país */
    data norm1;
        set &tabla;
        &campo = upcase(&campo);
        pais = substr(&campo,1,2);
        verificador = substr(&campo,3,2);
        resto = substr(&campo,5);
    run;

    /* Verificando pais y longitud */
    proc sql;
        create table norm2 as
        select a.*,
               longitud,
               case when length(&campo) ne longitud then 0
                    when b.codigo_pais = '' then 0
                    else 1 end as ind_valido
        from norm1 a
        left join IBAN b
        on a.pais = b.codigo_pais and a.pais = 'ES';
    quit;

    /* Verificar banco */
    proc sql;
        create table norm3 as
        select a.*,
               b.banco
        from norm2 a
        left join BANCOS b
        on substr(&campo,5,4) = input(put(b.codigo_banco,z4.),$4.);
    quit;

    data norm4;
        set norm3;
        if banco = '' then ind_valido = 0;
    run;

    /* validar codigo control */
    data norm5 (drop=calculo: modulo);
        set norm4;
        format calculo1 calculo2 calculo3 32.;
        if ind_valido = 1 and longitud = 24 then do;
            calculo1 = 0;
            calculo2 = 0;
            %do x = 5 %to 17;
                calculo1 = calculo1 * 10;
                if anyalpha(substr(&campo,&x,1)) = 1 then do;
                    calculo1 = calculo1 * 10;
                    calculo1 = calculo1 + rank(substr(&campo,&x,1))-55;
                end;
                else do;
                    calculo1 = calculo1 + put(substr(&campo,&x,1),8.);
                end;
            %end;
            modulo = mod(calculo1,97);
            %do x = 18 %to 24;
                calculo2 = calculo2 * 10;
                if anyalpha(substr(&campo,&x,1)) = 1 then do;
                    calculo2 = calculo2 * 10;
                    calculo2 = calculo2 + rank(substr(&campo,&x,1))-55;
                end;
                else do;
                    calculo2 = calculo2 + put(substr(&campo,&x,1),8.);
                end;
            %end;
            %do x = 1 %to 4;
                calculo2 = calculo2 * 10;
                if anyalpha(substr(&campo,&x,1)) = 1 then do;
                    calculo2 = calculo2 * 10;
                    calculo2 = calculo2 + rank(substr(&campo,&x,1))-55;
                end;
                else do;
                    calculo2 = calculo2 + put(substr(&campo,&x,1),8.);
                end;
            %end;
            calculo3 = (modulo * (10**ceil(log10(round(calculo2))))) + calculo2;
            modulo = mod(calculo3,97);
            if modulo ne 1 then do;
                ind_valido = 0;
                banco = "";
            end;
        end;
    run;

    /* salida */
    proc sql;
        create table &tabla._ as
        select a.*, ind_valido, banco
        from &tabla a
        left join norm5 b
        on a.&campo = b.&campo;
    quit;
%mend normalizar_ccc;

Descárgate este Excel que contiene los datos que debes incluir en la tabla PAISES y BANCOS:

El algoritmo para validar el IBAN partiendo del número de cuenta, consiste en lo siguiente:
1) Colocer los cuatro primeros caracteres detrás de todos los demás.
2) Sustituir todos los caracteres alfanuméricos que pueda haber por un equivalente numérico de 2 cifras de forma que A=10, B=11, C=12, etc.
3) Dividir la cifre restante entre 97 y tomar el resto de esa división.
4) Si el resto es un 1, entonces el IBAN es correcto.

Para validar la macro con algunos ejemplos de prueba, puedes utilizar lo siguiente:

data CUENTAS;
    format ccc $50.;
    length ccc $ 50;
    input ccc $;
    datalines;
ES6621000418401234567891
ES6000491500051234567892
ES9420805801101234567891
ES90002469125012345678910
ES7100302053091234567895
EL71003020530943574567895
ES1000492352082414205416
ES1720852066623456789011
ES4501986746463463463547
ES450198674646D463463547
ES450198674646&463463547
ES450198674646.463463547
ES1346657676431313200000
run;

%normalizar_ccc(tabla=CUENTAS,campo=ccc);

Macro SAS: Validar DNI de Perú

Hoy voy a incluir una macro que sirve para validar con SAS un número de DNI peruano verificando si el dígito verificador es correcto o no.
Hay que empezar diciendo que inicialmente, y hasta 2007, los DNIs sin fecha de caducidad tenían un dígito verificador que correspondía con una de estas letras: A, B, C, D, E, F, G, H, I, J o K. En el resto de casos se asigna un número de 0 a 9 que sirve para verificar el resto de dígitos del DNI a través de una serie de operaciones matemáticas.
Para trabajar con algunos ejemplos alimentaremos la macro con la siguiente tabla con DNIs a evaluar:

/* Ejemplos*/;
data DNIS;
    format DNI $10.;
    input DNI;
    datalines;
    67415321-0
    1657351-A
    31874-1
    671354134
run;

La macro %validarDNI añade dos campos a la tabla que se le pase por parámetro: dni_normal y ind_valido. dni_normal es el valor del campo que contenía el DNI originalmente, pero normalizado. ind_valido toma dos valores posibles: 1 que indica que el DNI es correcto y 0 que indica que es erróneo.
%validarDNI acepta además dos parámetros obligatorios: el nombre de la tabla y el nombre del campo DNI a validar dentro de ella.

/*Macro*/;
%macro validarDNI(tabla=, campo=);
    data DNI1 (drop=a valor resto codigo:);
        set &tabla;
        rename &campo=dni_original;
        dni_normal = upcase(compress(&campo,'-_. '));
        a = 9 - length(dni_normal);
        if a > 0 then dni_normal = compress(repeat('0',a-1) || dni_normal);
        ind_valido = 1;
        if length(dni_normal) > 9 then ind_valido = 0;
        %do i = 1 %to 8;
            if 0 > put(substr(dni_normal,&i,1),8.) or put(substr(dni_normal,&i,1),8.) > 9 then ind_valido = 0;
        %end;
        if substr(dni_normal,9,1) not in ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K') then ind_valido = 0;
        if ind_valido > 0 then do;
            valor = 3*put(substr(dni_normal,1,1),8.) +
                    2*put(substr(dni_normal,2,1),8.) +
                    7*put(substr(dni_normal,3,1),8.) +
                    6*put(substr(dni_normal,4,1),8.) +
                    5*put(substr(dni_normal,5,1),8.) +
                    4*put(substr(dni_normal,6,1),8.) +
                    3*put(substr(dni_normal,7,1),8.) +
                    2*put(substr(dni_normal,8,1),8.);
            resto = mod(valor,11);
            if resto = 0 then resto = 11;
            resto = resto + 1;
            codigo1 = substr('67890112345',resto,1);
            codigo2 = substr('KABCDEFGHIJ',resto,1);
            if substr(dni_normal,9) ne codigo1 and substr(dni_normal,9) ne codigo2 then ind_valido = 0;
        end;
    run;

    data &tabla;
        set DNI1;
        rename dni_original = &campo;
    run;
%mend;
%validarDNI(tabla=DNIS, campo=DNI);

El algoritmo para calcular el dígito verificador del DNI es el siguiente: se multiplica cada número del DNI normalizado (sus 8 primeros dígitos) por el dígito que ocupe la misma posición en la cadena: 1, 7, 8, 0, 1, 1, 4, 6 y luego se suman todos los factores para dar una cifra de la que calcularemos el resto con resto a dividirla entre 11 (aquí, si el resto es 0 tomaremos 11).
Restamos 11 menos la resultante de la operación anterior. Le sumaremos 1 y entonces tomaremos ese valor para buscar el dígito correspondiente a esa posición en la cadena: 6, 7, 8, 9, 0, 1, 1, 2, 3, 4, 5. Este último es el dígito de verificación.

Truco SAS: Ejecución condicionada con variables de sistema

En ocasiones me ha pasado que estoy trabajando en versionar o modificar un código que ya está planificado y ejecutándose actualmente. Por lo que tengo una versión del programa ejecutando en el servidor y otra versión que desarrollo en el SAS Enterprise Guide. Lo que me pasa es que hay partes que no quiero ejecutar en el Guide, bien porque tardan, bien porque no las necesito o porque solo se pueden ejecutar en el servidor.

Para no tener que estar ejecutando un trozo de código sí, otro no, hay un pequeño truco para dejarlo todo listo para que la misma versión de código pueda correr en ambos entornos pero haciendo en cada uno lo que necesito. Este truco recurre a las variables de sistema de SAS.

SYSUSERID es una variable de sistema de SAS que informa del usuario que está ejecutando. En una instalación típica de SAS, el usuario que utiliza SAS Guide cuando trabajamos con él es «sassrv». Cuando planificamos un proceso en el servidor se ejecuta con «sasbatch». Esta es la diferencia que explotaremos ene l siguiente ejemplo para lograr ejecutar una parte del código solamente en el servidor:

options mlogic;
%macro en_servidor;
    %if "&SYSUSERID"="sasbatch" %then %do;
        data a;
            a=1;
        run;
    %end;
%mend;

%en_servidor;

Si ejecutamos este código en el Guide no sucederá nada: no se creará la tabla A. He añadido la opcion options mlogic; que incluye en el log información sobre las decisiones lógicas que toma (el resultado de los %if). Si revisamos el log podremos ver que indica que el usuario NO ES sasbatch.

MLOGIC(EN_SERVIDOR):  Empezando la ejecución.
MLOGIC(EN_SERVIDOR):  la condición %IF "&SYSUSERID"="sasbacht" es FALSE
MLOGIC(EN_SERVIDOR):  finalizando la ejecución.

Si creamos otra macro en la que modificamos el código utilizando el usuario de SAS Guide (sassrv) veremos que sí se ejecuta:

options mlogic;
%macro en_guide;
    %if "&SYSUSERID"="sassrv" %then %do;
        data a;
            a=1;
        run;
    %end;
%mend;

%en_guide;

Ahora el resultado del %if sí es verdadero, y el paso data se ejecutará.

MLOGIC(EN_GUIDE):  Empezando la ejecución.
MLOGIC(EN_GUIDE): la condición %IF "&SYSUSERID"="sassrv" es VERDADERA
MPRINT(EN_GUIDE):   data a;
MPRINT(EN_GUIDE):   a=1;
MPRINT(EN_GUIDE):   run;

NOTE: The data set WORK.A has 1 observations and 1 variables.
NOTE: Sentencia DATA used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds

MLOGIC(EN_GUIDE):  finalizando la ejecución.

SAS: Bucles do y %do

En SAS se pueden utilizar tres tipos de bucles que además pueden utilizarse en código abierto o en lenguaje macro. Los tipos de bucles son: do, while y until. Lo relevante es que el uso de estos bucles en código abierto o lenguaje macro es muy diferente.

Lo primero que hay que saber es que los bucles de macro solo se pueden utilizar dentro de una macro y debido al momento en que se resuelve el código hay que tener ciertas consideraciones.

código abierto lenguaje macro

do x=1 to 10 by 1;
[comandos]
end;

%do x=1 %to 10 %by 1;
[comandos]
%end;

El bucle do (y %do) repite la ejecución de una parte de un código un número de veces que va representado por el valor de la variable indicada (x, en este caso) que comienza en 1 y termina con valor 10, saltando valores de 1 en 1. Tanto 1 como 10 se ejecutan dentro del bucle.

La diferencia entre usar el bucle do con código abierto o lenguaje macro está en que la x que se itera, es un campo en código abierto (que saldrá en la tabla de destino) y una macrovariable en lenguaje macro. Esto afecta a la forma de uso:

código abierto lenguaje macro

data salida1;
    do x=1 to 10;
        a=x;
        output;
    end;
run;

%macro iter_do;
    data  salida2;
        %do x=1 %to 10;
                a=&x;
                output;
        %end;
    run;
%mend;
%iter_do;

Hay también limitaciones en lenguaje macro en el sentido en que no se puede incluir como condición del bucle una referencia o cálculo sobre un campo de la tabla de entrada. La razón es que el código SAS que se va a ejecutar se «escribe» en el momento de interpretar la macro, pero no se ejecuta, por lo que la referencia al contenido de la tabla no existe aún. En este caso, por ejemplo, donde se usa el campo «a» de la tabla que antes hemos creado:

%macro iter_do;
    data  salida2;
        set salida1;
        %do x=1 %to a;
                a=&x;
                output;
        %end;
    run;
%mend;
%iter_do;

Obtenemos como salida un error del tipo:

"ERROR: A character operand was found in the %EVAL function or 
%IF condition where a numeric operand is required. The condition was: a".

Se puede superar este error declarando una macrovariable que tome el valor de "a" antes del inicio del bucle, pero no puede hacer referencia a un valor que no sea constante para todos los registros de la tabla. Si esto te pasa es síntoma de que tienes que estar utilizando un bucle do, no uno %do.

En otras ocasiones en el %to se incluyen ciertos cálculos que no son resueltos correctamente en la declaración del bucle. Esto puede requerir declarar una macrovariable previamente o utilizar un %sysfunc() por cada función de SAS que estemos utilizando. %sysfunc permite utilizar la mayoría de las funciones SAS en tiempo de ejecución macro. (Existen notables excepciones como put, que no admite %sysfunc). Un ejemplo de esto sería:

%let inicio=22000;  /* Fecha de 26mar2020 */;

%macro iter_do;
    data  salida3;
        %do x=&inicio %to date();
                a=put(&x,date9.);
                output;
        %end;
    run;
%mend;
%iter_do;

Que daría un error como el que sigue:

ERROR: Required operator not found in expression: date() 
ERROR: The %TO value of the %DO X loop is invalid.
ERROR: The macro ITER_DO will stop executing.

La solución es usar %sysfunc(date()). Es necesario usar un %sysfunc para cada función de SAS que esté incluida en la declaración del bucle de forma que se anidarán unas dentro de otras tanto como sea necesario. Atención a los paréntesis en las estructuras más complejas.

Punteros de datos en SAS

Bajo mi opinión los punteros de datos son una de las herramientas más potentes que tenemos. Me gusta mucho usarlos y seguro los veréis en en muchas de las macros que voy creando. Siempre llega un punto en que es más fácil utilizar un puntero para recorrer una tabla mientras se está en un paso data con otra tabla. Esta forma de trabajo está recomendada para evitar tener que hacer productos cartesianos cuando las tablas son muy grandes, ya que ahora muchísimo espacio y también recursos de máquina.

Un puntero de datos se declara con una sentencia open que abre la tabla indicada crea el stream de datos; devuelve un código de respuesta en función del resultado de la operación.

%let dsid = %sysfunc(open(&tabla));

Usualmente los primero que hacemos es valorar el tamaño de la tabla, probablemente para poder recorrerla con bucles. Para ellos tenemos aquí dos atributos útiles: nlobs y nvars; para calcular el número de observaciones y de columnas de una tabla, respectibamente. Cuidado aquí con otro atributo parecido: nobs, que devuelve el número de registros físicos, no los lógicos. La diferencia estriba en que los registros marcados para borrar en esa tabla no serán contabilizados por nlobs

%let nobs =%sysfunc(attrn(&dsid,nlobs));
%let nvars=%sysfunc(attrn(&dsid,nvars));

Antes de recorrer la tabla tenemos dos atributos útiles: fetch, que lee el siguiente registro no eliminado de la tabla y fetchobs que sitúa el cursor en el número de registro indicado:

%let rc=%sysfunc(fetch(&dsid));
%let rc=%sysfunc(fetchobs(&dsid,1));

Ambas rutinas devuelven 0 en caso de éxito, -1 en caso de haber llegado al final del fichero y cualquier otro valor en caso de algún tipo de error. El error se puede recogar con %sysfunc(sysmsg()).

La consulta la vector de datos la haremos del siguiente modo y dependiendo de si la variables es una cadena o un número, por supuesto las fechas son números. Primero utilizamos varnum para identificar el número de variable dentro de la tabla y luego getvarc o getvarn para obtener su valor:

%let variable_num = %sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,variable_num))));
%let variable_char = %sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,variable_char))));

Finalmente es importante cerrar el puntero después de usarlo con el comando close>/code>:

%let rc = %sysfunc(close(&dsid));

Con todo ello tenemos las herramientas para poder utilizar vectores de datos con SAS.

Automatización del borrado de ficheros

La automatización del borrado de ficheros es una tarea necesaria cuando, por ejemplo, estamos generando en un proceso planificado ficheros fechados periódicamente. Hacer exhaustivamente esta tarea es complicado y no siempre la realizamos por esa razón (yo al menos). Esta macro da solución a esa necesidad, os animo a usarla.

Lo primero que hacemos es crear un stream de datos, df, que recoge el resultado del comando ls. El parámetro F marca los directorios con una barra «/» al final, de forma que podemos eliminarlos de la salida del ls con un grep -v.

Usando df como entrada para nuestro paso data todo lo que queda es procesar las información que hemos generado con un scan, que divide la cadena por los espacios. Finalmente solo nos queda montar la fecha de creación del fichero teniendo en cuenta lo siguiente: Linux devuelve la fecha en dos formatos distintos dependiendo de si hace menos de 6 meses que se ha generado el fichero o si hace más. Este es un ejemplo:

Formatos de fecha en un ls

Finalmente, filtramos los registros que contienen los nombres de los ficheros que queremos eliminar: ficheros anteriores a 2020 y que no sean tablas SAS. Un call system ejemcutará un comando rm con cada registro resultante. Esta es la macro:

%macro borrado(ruta=,fecha_limite=);
    x "cd &ruta";
    filename df pipe "ls -lahF &ruta | grep -v /";

    data _null_;
        infile df;
        input todo $300.;
        format fecha date9. fichero $100.;
        dia = scan(todo,7," ");
        if index(scan(todo,8," "),":") then anyo=year(date());
           else anyo=input(put(scan(todo,8," "),$5.),8.);
        if compress(scan(todo,6," "))='Jan' then mes=1;
        else if compress(scan(todo,6," "))='Feb' then mes=2;
        else if compress(scan(todo,6," "))='Mar' then mes=3;
        else if compress(scan(todo,6," "))='Apr' then mes=4;
        else if compress(scan(todo,6," "))='May' then mes=5;
        else if compress(scan(todo,6," "))='Jun' then mes=6;
        else if compress(scan(todo,6," "))='Jul' then mes=7;
        else if compress(scan(todo,6," "))='Aug' then mes=8;
        else if compress(scan(todo,6," "))='Sep' then mes=9;
        else if compress(scan(todo,6," "))='Oct' then mes=10;
        else if compress(scan(todo,6," "))='Nov' then mes=11;
        else if compress(scan(todo,6," "))='Dec' then mes=12;
        fecha = mdy(mes,dia,anyo);
        if fecha > date() then fecha=intnx('year',fecha,-1,'S');
        fichero = compress(scan(todo,9," "),'*');
        if fecha < &fecha_limite and not index(fichero,'.sas7bdat');

        call system('rm '||fichero);
    run;
%mend;

%borrado(ruta=[rute],fecha_limite='1jan2020'd);

Comprobar si existe un campo en una tabla SAS

En ocasiones necesitamos saber si existe un fichero o una tabla y para ello utilizamos la función exist, pero ¿qué pasa si queremos saber si en la tabla que estamos procesando hay un determinado campo?

Es una situación menos habitual y lo malo es que no existe una función para evaluarlo directamente, así que nos toca remangarnos y usar un poco nuestra imaginación.

Existen varias soluciones para abordar esto, seguro que cada una más adecuada en según qué circunstancias. En este caso vamos a evaluar si en la tabla SASHELP.US_DATA tenemos el campo «capital» y os propongo varias soluciones:

La primera que se me ocurre es utilizando vectores:

%macro comprobar_variable1(campo_buscado=);
    %let dsid = %sysfunc(open(SASHELP.US_DATA));
    %let rc = %sysfunc(fetchobs(&dsid,1));
    %let r1 = %sysfunc(varnum(&dsid,&campo_buscado));
    %let r2 = %sysfunc(varnum(&dsid,&campo_buscado));
    %let resultado = %sysfunc(max(&r1>0,&r2>0));
    %put &=resultado;
    %let rc = %sysfunc(close(&dsid));
%mend;

%comprobar_variable1(campo_buscado=CAPITAL);
%comprobar_variable1(campo_buscado=region);

Otra solución aparentemente más sencilla de programar sería utilizando proc contents. A mi me resulta muy útil este procedimiento para resolver muchos problemas de automatización de procesos.

%macro comprobar_variable2(campo_buscado=);
    proc contents data=SASHELP.US_DATA out=VARIABLES (keep=name) noprint;
    run;

    proc sql noprint;
        select count(1) into :resultado
        from VARIABLES
        where name=upcase("&campo_buscado");
    quit;

    %put &=resultado;
%mend;
%comprobar_variable2(campo_buscado=CAPITAL);
%comprobar_variable2(campo_buscado=region);

Os propongo que enviéis alguna más que se os pueda ocurrir.