<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: Retrieve variables from dataset affected by keep/drop within macro in SAS Programming</title>
    <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966959#M376248</link>
    <description>&lt;BLOCKQUOTE&gt;&lt;HR /&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/159"&gt;@Tom&lt;/a&gt;&amp;nbsp;wrote in part:&lt;BR /&gt;
&lt;P&gt;NOTE the %GLOBAL statement.&amp;nbsp; You cannot return macro variables from DOSUBL() into local macro variables.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;DOSUBL() can return side session global macro variables into main session local macro variables.&amp;nbsp; In this case, you can't use the %GLOBAL statement in the side session to create the macro variable, because it will throw (I think incorrectly):&lt;/P&gt;
&lt;PRE&gt;ERROR: Attempt to %GLOBAL a name (VARLIST) which exists in a local environment.
&lt;/PRE&gt;
&lt;P&gt;But if you add an open-code %let statement inside the DOSUBL call, it will happily create a side session global macro variable, which will be returned to the main session local symbol table.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;So code like:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;%macro list_vars_step(dataset,mvar=varlist);
%if not %symexist(&amp;amp;mvar) %then %global &amp;amp;mvar;
%let &amp;amp;mvar=;
data _null_;
  if 0 then set &amp;amp;dataset;
  do while(1=1);
    length ___NAME___ $32 ;
    call vnext(___NAME___);
    if ___NAME___='___NAME___' then stop;
    if ' '=resolve('%let &amp;amp;mvar=&amp;amp;&amp;amp;&amp;amp;mvar '||___NAME___||';') then;
  end;
  stop;
run;
%mend list_vars_step;


%macro list_vars(dataset);
%local rc vars dsid i;
%if %sysfunc(indexc(&amp;amp;dataset,())) %then %do;
  %local varlist;
  %let rc=%sysfunc(dosubl(%nrstr(%let varlist=;%list_vars_step(&amp;amp;dataset) %put INSIDE DOSUBL ; %put _user_ ; %put ; )));  /*added code here*/
  %put AFTER DOSUBL ; %put _user_ ; %put ;  /*added code here*/
  %let vars=&amp;amp;varlist;
%end;
%else %do;
  %let dsid = %sysfunc(open(&amp;amp;dataset));
  %do i=1 %to %sysfunc(attrn(&amp;amp;dsid, nvar));
    %let vars=&amp;amp;vars %sysfunc(varname(&amp;amp;dsid,&amp;amp;i));
  %end;
  %let dsid=%sysfunc(close(&amp;amp;dsid));
%end;
&amp;amp;vars.
%mend list_vars;

%put %list_vars(sashelp.class(drop=sex));&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Returns:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;33   %mend list_vars;

INSIDE DOSUBL
GLOBAL VARLIST Name Age Height Weight
LIST_VARS DATASET sashelp.class(drop=sex)
LIST_VARS DSID
LIST_VARS I
LIST_VARS RC
LIST_VARS VARLIST
LIST_VARS VARS

34
35   %put %list_vars(sashelp.class(drop=sex));
AFTER DOSUBL
LIST_VARS DATASET sashelp.class(drop=sex)
LIST_VARS DSID
LIST_VARS I
LIST_VARS RC 0
LIST_VARS VARLIST Name Age Height Weight
LIST_VARS VARS

Name Age Height Weight
&lt;/PRE&gt;
&lt;P&gt;The log is a bit out of order, but you can see that inside the DOSUBL block there is a global macro variable VARLIST,&amp;nbsp; the value of which is returned the local macro variable when DOSUBL completes.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Mon, 19 May 2025 20:19:26 GMT</pubDate>
    <dc:creator>Quentin</dc:creator>
    <dc:date>2025-05-19T20:19:26Z</dc:date>
    <item>
      <title>Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966682#M376200</link>
      <description>&lt;P&gt;I'm wondering if the very specific thing I'm trying to do is possible; there's other ways to solve this (see below) so it's more a question of efficiency than anything.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;For background, this is to be part of a macro that writes partial data step statements, so I have one absolute requirement: &lt;STRONG&gt;no step boundaries&lt;/STRONG&gt;. With that in mind, I'm trying to efficiently get a list of variables on some dataset, with the kicker that this dataset can ideally be affected by clauses such as &lt;FONT face="courier new,courier"&gt;dataset(drop=...)&lt;/FONT&gt;. Here's a toy macro to illustrate:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=""&gt;%MACRO LIST_VARS(dataset);
   %let dsid = %sysfunc(open(&amp;amp;dataset));
   %let vars = ;
   %do i = 1 %to %sysfunc(attrn(&amp;amp;dsid, nvar));
      %let vars = &amp;amp;vars %sysfunc(varname(&amp;amp;dsid, &amp;amp;i));
   %end;&lt;BR /&gt;   %let dsid = %sysfunc(close(&amp;amp;dsid));
   &amp;amp;vars
%MEND;

/* Example use -- no clauses, no issue. */
%put %LIST_VARS(sashelp.class);
* Name Sex Age Height Weight ;&lt;/CODE&gt;&lt;/PRE&gt;&lt;P&gt;The problem is that if you include &lt;FONT face="courier new,courier"&gt;drop=&lt;/FONT&gt; or &lt;FONT face="courier new,courier"&gt;keep=&lt;/FONT&gt; clauses, the number of variables will be affected but their &lt;EM&gt;variable number&lt;/EM&gt; will not:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=""&gt;/* Three variables retained: Name (#1), nAtBat (#3), and nAssts (#20) */
%put %LIST_VARS(sashelp.baseball(keep=na:));
* WARNING: Argument 2 to function VARNAME is out of range. ; /* Fires for varname(2) */
* Name nAtBat                                              ;&amp;nbsp;/* Note: no nAssts      */ &lt;/CODE&gt;&lt;/PRE&gt;&lt;P&gt;Specifically, while I'd assume the three variables can be retrieved as &lt;FONT face="courier new,courier"&gt;varname(1, 2, 3)&lt;/FONT&gt; they actually still occupy numbers 1, 3, 20 in this dataset as can be checked via &lt;FONT face="courier new,courier"&gt;PROC CONTENTS&lt;/FONT&gt;:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=""&gt;proc contents data=sashelp.baseball(keep=na:) varnum;
quit;
*  #  Variable  ;
*  1  Name      ;
*  3  nAtBat    ;
* 20  nAssts    ;&lt;/CODE&gt;&lt;/PRE&gt;&lt;P&gt;This creates a bit of a catch-22: to get these &lt;FONT face="courier new,courier"&gt;varnum&lt;/FONT&gt; I'd need the &lt;FONT face="courier new,courier"&gt;varname&lt;/FONT&gt;, and to get the &lt;FONT face="courier new,courier"&gt;varname&lt;/FONT&gt; I need the &lt;FONT face="courier new,courier"&gt;varnum&lt;/FONT&gt;. It is a brute-force option to keep incrementing variable numbers within &lt;FONT face="courier new,courier"&gt;varname&lt;/FONT&gt; until &lt;FONT face="courier new,courier"&gt;nvar&lt;/FONT&gt; (3) non-missing names are returned, but that isn't exactly elegant and will result in a bunch of warnings which I'd rather avoid. So, my question: is there any way to do this, i.e. how to efficiently figure out in this example that it's variables 1, 3, and 20 that were retained, in macro language?&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;---&lt;/P&gt;&lt;P&gt;To be sure, I've already worked around this issue in the following ways: (i) implement drop/keep functionality in pure macro (i.e. read the full variable list and subset manually) but I'd rather &lt;EM&gt;also&lt;/EM&gt; support dataset clauses, and (ii) dump the clauses into a side-session view via &lt;FONT face="courier new,courier"&gt;%sysfunc(dosubl)&lt;/FONT&gt; to then process that, but that causes quite some performance overhead. The same would hold for running e.g. &lt;FONT face="courier new,courier"&gt;PROC CONTENTS&lt;/FONT&gt; in a side session. A third option could be to first read in the full dataset, get all variables, and then handle the clauses separately, but that would also require a lot of housekeeping and manual processing. Surely there is some quick function or trick that I'm missing?&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 16 May 2025 13:55:44 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966682#M376200</guid>
      <dc:creator>pblls</dc:creator>
      <dc:date>2025-05-16T13:55:44Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966686#M376203</link>
      <description>&lt;P&gt;Extract the list of variable names from SASHELP.VCOLUMN&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Example:&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc sql;
    select name from sashelp.vcolumn where libname='SASHELP' and memname='BASEBALL';
    quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;or if you just want variable names that begin with NA&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc sql;
    select name from sashelp.vcolumn where libname='SASHELP' and memname='BASEBALL'
    and upcase(name) eqt 'NA';
    quit;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;This should be easy to turn into a macro, if that's what you want.&lt;/P&gt;</description>
      <pubDate>Fri, 16 May 2025 15:05:16 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966686#M376203</guid>
      <dc:creator>PaigeMiller</dc:creator>
      <dc:date>2025-05-16T15:05:16Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966699#M376204</link>
      <description>&lt;P&gt;&lt;SPAN&gt;Can you elaborate on how much performance overhead you anticipate.&amp;nbsp; &amp;nbsp; Are you going to call this macro thousands of times.&amp;nbsp; From your description (&lt;EM&gt;this is to be part of a macro that writes partial data step statements)&lt;/EM&gt; it seems to me that a few calls to DOSUBL to build the data step code would have acceptable performance.&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;SPAN&gt;(ii) dump the clauses into a side-session view via&amp;nbsp;&lt;/SPAN&gt;&lt;FONT face="courier new,courier"&gt;%sysfunc(dosubl)&lt;/FONT&gt;&lt;SPAN&gt;&amp;nbsp;to then process that, but that&lt;STRONG&gt; causes quite some performance overhead&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;</description>
      <pubDate>Fri, 16 May 2025 16:16:32 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966699#M376204</guid>
      <dc:creator>data_null__</dc:creator>
      <dc:date>2025-05-16T16:16:32Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966753#M376213</link>
      <description>&lt;P&gt;You can not retrieve the variable name with option DROP= KEEP= , why not create a temporary dataset ?&lt;/P&gt;
&lt;PRE&gt;%MACRO LIST_VARS(dataset);
&lt;STRONG&gt;data temp;
   set &amp;amp;dataset ;
 run;
   %let dsid = %sysfunc(open(temp));&lt;/STRONG&gt;
   %let vars = ;
   %do i = 1 %to %sysfunc(attrn(&amp;amp;dsid, nvar));
      %let vars = &amp;amp;vars %sysfunc(varname(&amp;amp;dsid, &amp;amp;i));
   %end;
   %let dsid = %sysfunc(close(&amp;amp;dsid));
/*   &amp;amp;vars*/
   %put &amp;amp;=vars ;
%MEND;

/* Example use -- no clauses, no issue. */
%LIST_VARS(sashelp.class(drop=name));&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;If you really want to work with DROP= KEEP=, try function VNEXT().&lt;/P&gt;
&lt;PRE&gt;%MACRO LIST_VARS(dataset);
   data _null_;
    set &amp;amp;dataset.;
    length __vname__ $ 40 __list__ $ 2000;
	do until(lowcase(__vname__) = '__vname__');
	 __list__=catx(' ',__list__,__vname__);
     call vnext(__vname__);
	end;
	call symputx('vars',__list__,'G');
	stop;
   run;
%put &amp;amp;=vars;
/*   &amp;amp;vars*/
%MEND;

/* Example use -- no clauses, no issue. */
%LIST_VARS(sashelp.class(drop=name));&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Sat, 17 May 2025 02:51:25 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966753#M376213</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2025-05-17T02:51:25Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966786#M376232</link>
      <description>Thanks, but this doesn't address the main goal: there should be no step boundaries (the macro should not end an already-ongoing data step). That can be worked around by throwing the PROC SQL into a side session, though that just makes it a slightly worse version of the solution I already have...</description>
      <pubDate>Mon, 19 May 2025 06:33:18 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966786#M376232</guid>
      <dc:creator>pblls</dc:creator>
      <dc:date>2025-05-19T06:33:18Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966788#M376234</link>
      <description>&lt;P&gt;Fair, this is nice-to-have rather than performance-critical; the DOSUBL is still easily 95+% of the processing time (see example below). This introduces a bit of additional overhead because I use a separate library to avoid conflicts with "user space". While there is a bit of post-processing of the variable list it comes nowhere near the additional time needed for the side session steps. And indeed, the macro may end up getting called several hundreds or thousands of times over a large set of programs, at which point shaving half a second &lt;EM&gt;from each iteration&lt;/EM&gt; will add up. Or at least, with my current solution it becomes less competitive to macro this up versus writing the code out by hand...&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;&lt;CODE class=""&gt;/* Desired version (but doesn't support clauses). */
%MACRO LIST_VARS(dataset);
   %local dsid vars i;
   %let dsid = %sysfunc(open(&amp;amp;dataset));
   %do i = 1 %to %sysfunc(attrn(&amp;amp;dsid, nvar));
      %let vars = &amp;amp;vars %sysfunc(varname(&amp;amp;dsid, &amp;amp;i));
   %end;
   %let dsid = %sysfunc(close(&amp;amp;dsid));
   &amp;amp;vars
%MEND;

/* Supports clauses, loses much performance. */
%MACRO LIST_VARS_DOSUBL(dataset);
   %local rc;
   %let rc = %sysfunc(dosubl(%nrstr(
      options dlcreatedir nonotes;
      libname templib "%sysfunc(getoption(work))/templib" compress=yes;
      data templib.tempds / view=templib.tempds;
         set %unquote(&amp;amp;dataset);
      run;
   )));
   %let rc = %sysfunc(libname(TEMPLIB, %sysfunc(getoption(work))/templib,, compress=yes));
   %LIST_VARS(TEMPLIB.TEMPDS)
   %let rc = %sysfunc(libname(TEMPLIB));
%MEND;

/* Benchmark */
%MACRO TIME_BOTH(dataset, its=10);
   %local _ j t0 t1 t2;
   %let t0 = %sysfunc(datetime());
   %do j = 1 %to &amp;amp;its;
      %let _ = %LIST_VARS(&amp;amp;dataset);
   %end;
   %let t1 = %sysfunc(datetime());
   %do j = 1 %to &amp;amp;its;
      %let _ = %LIST_VARS_DOSUBL(&amp;amp;dataset);
   %end;
   %let t2 = %sysfunc(datetime());
   
   %put FIRST VERSION TOOK  %sysfunc(putn(%sysevalf(&amp;amp;t1 - &amp;amp;t0), tod12.5));
   %put SECOND VERSION TOOK %sysfunc(putn(%sysevalf(&amp;amp;t2 - &amp;amp;t1), tod12.5));
%MEND;

%TIME_BOTH(sashelp.class);
* FIRST VERSION TOOK  0:00:00.016 ;
* SECOND VERSION TOOK 0:00:01.568 ;&lt;/CODE&gt;&lt;/PRE&gt;</description>
      <pubDate>Mon, 19 May 2025 07:04:08 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966788#M376234</guid>
      <dc:creator>pblls</dc:creator>
      <dc:date>2025-05-19T07:04:08Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966789#M376235</link>
      <description>&lt;P&gt;Thanks, but this doesn't address the main requirement: the macro cannot end an ongoing data step. Throwing this into a side session makes this basically the solution I have now because there isn't really a need to actually &lt;EM&gt;process&lt;/EM&gt; the clauses, just compile.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;If there is a way to use something like &lt;FONT face="courier new,courier"&gt;call vnext&lt;/FONT&gt; in pure macro after a &lt;FONT face="courier new,courier"&gt;%sysfunc(open))&lt;/FONT&gt; call, that would be exactly what I'm looking for!&lt;/P&gt;</description>
      <pubDate>Mon, 19 May 2025 07:08:54 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966789#M376235</guid>
      <dc:creator>pblls</dc:creator>
      <dc:date>2025-05-19T07:08:54Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966939#M376239</link>
      <description>&lt;P&gt;It is not just that it complains about the varnum that is not there. The NVAR dataset attribute also does not provide the right number of variables needed to find all of the names that are there.&lt;/P&gt;
&lt;PRE&gt;25   %put %LIST_VARS(sashelp.class);
1=Name 2=Sex 3=Age 4=Height 5=Weight
26   %put %LIST_VARS(sashelp.class(drop=sex));
WARNING: Argument 2 to function VARNAME referenced by the %SYSFUNC or
         %QSYSFUNC macro function is out of range.
1=Name 2= 3=Age 4=Height
&lt;/PRE&gt;</description>
      <pubDate>Mon, 19 May 2025 12:50:57 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966939#M376239</guid>
      <dc:creator>Tom</dc:creator>
      <dc:date>2025-05-19T12:50:57Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966940#M376240</link>
      <description>&lt;P&gt;What I have done is just add my own note to the log so users know they can ignore the warning.&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;%macro list_vars(dataset);
%local dsid var i n ;
%let dsid = %sysfunc(open(&amp;amp;dataset));
%let n = %sysfunc(attrn(&amp;amp;dsid, nvar));
%do %until(&amp;amp;i &amp;gt;= &amp;amp;n);
  %let i=%eval(&amp;amp;i+1);
  %let var=%sysfunc(varname(&amp;amp;dsid,&amp;amp;i));
  %if %length(&amp;amp;var) %then %sysfunc(nliteral(&amp;amp;var));
  %else %do;
    %let n=%eval(&amp;amp;n+1);
    %put NOTE: Warning from VARNAME function is because of dropped variables ;
  %end;
%end;
%let dsid = %sysfunc(close(&amp;amp;dsid));
%mend list_vars;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Example&lt;/P&gt;
&lt;PRE&gt;16
17   /* Example */
18   %put %list_vars(sashelp.class);
Name Sex Age Height Weight
19   %put %list_vars(sashelp.class(drop=sex));
WARNING: Argument 2 to function VARNAME referenced by the %SYSFUNC or
         %QSYSFUNC macro function is out of range.
NOTE: Warning from VARNAME function is because of dropped variables
Name Age Height Weight
&lt;/PRE&gt;</description>
      <pubDate>Mon, 19 May 2025 13:16:45 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966940#M376240</guid>
      <dc:creator>Tom</dc:creator>
      <dc:date>2025-05-19T13:16:45Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966948#M376245</link>
      <description>&lt;P&gt;This isn't entirely accurate -- the problem in your example is that you're querying four variables (&lt;FONT face="courier new,courier"&gt;nvar&lt;/FONT&gt; = 4) but they are indices 1, 3, 4, 5 while the loop goes 1, 2, 3, 4. Sex (#2) isn't on the dataset anymore, that's what gives the warning, that's what causes the empty return in that slot. If you were to drop the fifth variable, Weight, this example would work without issue, but only because the &lt;FONT face="courier new,courier"&gt;varnum&lt;/FONT&gt; would remain consecutive then.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Your solution is to keep looping until you retrieve the expected number of &lt;FONT face="courier new,courier"&gt;varname&lt;/FONT&gt;, but I'd rather have no warnings at all &lt;span class="lia-unicode-emoji" title=":disappointed_face:"&gt;😞&lt;/span&gt;&lt;BR /&gt;(it's also not very efficient if you keep few variables on a very large dataset)&lt;/P&gt;</description>
      <pubDate>Mon, 19 May 2025 15:12:53 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966948#M376245</guid>
      <dc:creator>pblls</dc:creator>
      <dc:date>2025-05-19T15:12:53Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966949#M376246</link>
      <description>&lt;P&gt;Today I would also use DOSUBL for this.&amp;nbsp; But before there was DOSUBL, I hit this problem.&amp;nbsp; I see it as a bug that that VARNAME function does not honor the KEEP/DROP option.&amp;nbsp; I've been told in that in SCL, it does honor KEEP/DROP.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Luckly, Søren Lassen posted some solutions to SAS-L.&amp;nbsp; Basic approach is to open the dataset twice, once with the keep/drop option, and once without.&amp;nbsp; With that, you can loop over the list of variables without the keep/drop applied, and use the VARNUM function to test if the variable exists in the dataset with the keep/drop applied.&amp;nbsp; Code like:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;%macro ListVars(data);

/***
Based on %VarList macro developed by Søren Lassen
Posted to SAS-L December 2010 and May 4, 2015

Macro is complicated by fact that the varname function does not honor keep/drop option.
Therefore need workaround to prevent er ror msg if user specifies keep/drop.  
The work around is to iterate over list of variables in dataset
without keep/drop applied, and check if each variable is in the dataset
with keep/drop applied.
***/


%local
  dsid   /*id of &amp;amp;data, with    any keep/drop options*/
  dsid2  /*id of &amp;amp;data, without any keep/drop options*/
  i 
  var    /*name of ith variable in &amp;amp;data*/
  return 
  rc
;


%*If &amp;amp;data had a rename option, macro would not return the renamed variables;
%*So user is not allowed to use rename option.;
%if %sysfunc(find(&amp;amp;data,rename,i)) %then %do;
  %put ER%str()ROR: (%nrstr(%%)&amp;amp;sysmacroname) cannot include rename option on dataset.;
  %goto mexit;
%end;

%let dsid=%sysfunc(open(&amp;amp;data));                     /*dataset with any keep/drop options*/
%let dsid2=%sysfunc(open(%scan(&amp;amp;data,1,%str(%( )))); /*dataset without keep/drop options*/


%do i=1 %to %sysfunc(attrn(&amp;amp;dsid2,nvars));

  %let var=%sysfunc(varname(&amp;amp;dsid2,&amp;amp;i));

  %if (%sysfunc(varnum(&amp;amp;dsid,&amp;amp;var))) %then
    %do;
      %if %superq(return) = %str() %then %let return=&amp;amp;var;
      %else %let return=&amp;amp;return &amp;amp;var;
    %end;
%end;

%let rc=%sysfunc(close(&amp;amp;dsid));
%let rc=%sysfunc(close(&amp;amp;dsid2));

&amp;amp;return

%mexit:
%mend ListVars;

%put %listvars(sashelp.class) ;
%put %listvars(sashelp.class(keep=_numeric_)) ;
%put %listvars(sashelp.class(drop=_character_)) ;&lt;/CODE&gt;&lt;/PRE&gt;</description>
      <pubDate>Mon, 19 May 2025 15:44:07 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966949#M376246</guid>
      <dc:creator>Quentin</dc:creator>
      <dc:date>2025-05-19T15:44:07Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966950#M376247</link>
      <description>&lt;P&gt;So you can combine the two.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Say you have coded a macro that can get the answer when there are dataset options, but it requires generating a step boundary.&amp;nbsp; Say something like this:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;%macro list_vars_step(dataset,mvar=varlist);
%if not %symexist(&amp;amp;mvar) %then %global &amp;amp;mvar;
%let &amp;amp;mvar=;
data _null_;
  if 0 then set &amp;amp;dataset;
  do while(1=1);
    length ___NAME___ $32 ;
    call vnext(___NAME___);
    if ___NAME___='___NAME___' then stop;
    if ' '=resolve('%let &amp;amp;mvar=&amp;amp;&amp;amp;&amp;amp;mvar '||___NAME___||';') then;
  end;
  stop;
run;
%mend list_vars_step;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now you can make your new macro to return the list of names without a step boundary use that macro in a DOSUBL() side session only when there are dataset options.&amp;nbsp; Otherwise just use the VARNAME() function.&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;%macro list_vars(dataset);
%local rc vars dsid i;
%if %sysfunc(indexc(&amp;amp;dataset,())) %then %do;
  %global varlist;
  %let rc=%sysfunc(dosubl(%nrstr(options nonotes;%list_vars_step(&amp;amp;dataset))));
  %let vars=&amp;amp;varlist;
%end;
%else %do;
  %let dsid = %sysfunc(open(&amp;amp;dataset));
  %do i=1 %to %sysfunc(attrn(&amp;amp;dsid, nvar));
    %let vars=&amp;amp;vars %sysfunc(varname(&amp;amp;dsid,&amp;amp;i));
  %end;
  %let dsid=%sysfunc(close(&amp;amp;dsid));
%end;
&amp;amp;vars.
%mend list_vars;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;NOTE the %GLOBAL statement.&amp;nbsp; You cannot return macro variables from DOSUBL() into local macro variables.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;339  /* Example */
340  %put %list_vars(sashelp.class);
Name Sex Age Height Weight
341  %put %list_vars(sashelp.class(drop=sex));
Name Age Height Weight
&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 19 May 2025 19:05:27 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966950#M376247</guid>
      <dc:creator>Tom</dc:creator>
      <dc:date>2025-05-19T19:05:27Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966959#M376248</link>
      <description>&lt;BLOCKQUOTE&gt;&lt;HR /&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/159"&gt;@Tom&lt;/a&gt;&amp;nbsp;wrote in part:&lt;BR /&gt;
&lt;P&gt;NOTE the %GLOBAL statement.&amp;nbsp; You cannot return macro variables from DOSUBL() into local macro variables.&lt;/P&gt;
&lt;/BLOCKQUOTE&gt;
&lt;P&gt;DOSUBL() can return side session global macro variables into main session local macro variables.&amp;nbsp; In this case, you can't use the %GLOBAL statement in the side session to create the macro variable, because it will throw (I think incorrectly):&lt;/P&gt;
&lt;PRE&gt;ERROR: Attempt to %GLOBAL a name (VARLIST) which exists in a local environment.
&lt;/PRE&gt;
&lt;P&gt;But if you add an open-code %let statement inside the DOSUBL call, it will happily create a side session global macro variable, which will be returned to the main session local symbol table.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;So code like:&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;%macro list_vars_step(dataset,mvar=varlist);
%if not %symexist(&amp;amp;mvar) %then %global &amp;amp;mvar;
%let &amp;amp;mvar=;
data _null_;
  if 0 then set &amp;amp;dataset;
  do while(1=1);
    length ___NAME___ $32 ;
    call vnext(___NAME___);
    if ___NAME___='___NAME___' then stop;
    if ' '=resolve('%let &amp;amp;mvar=&amp;amp;&amp;amp;&amp;amp;mvar '||___NAME___||';') then;
  end;
  stop;
run;
%mend list_vars_step;


%macro list_vars(dataset);
%local rc vars dsid i;
%if %sysfunc(indexc(&amp;amp;dataset,())) %then %do;
  %local varlist;
  %let rc=%sysfunc(dosubl(%nrstr(%let varlist=;%list_vars_step(&amp;amp;dataset) %put INSIDE DOSUBL ; %put _user_ ; %put ; )));  /*added code here*/
  %put AFTER DOSUBL ; %put _user_ ; %put ;  /*added code here*/
  %let vars=&amp;amp;varlist;
%end;
%else %do;
  %let dsid = %sysfunc(open(&amp;amp;dataset));
  %do i=1 %to %sysfunc(attrn(&amp;amp;dsid, nvar));
    %let vars=&amp;amp;vars %sysfunc(varname(&amp;amp;dsid,&amp;amp;i));
  %end;
  %let dsid=%sysfunc(close(&amp;amp;dsid));
%end;
&amp;amp;vars.
%mend list_vars;

%put %list_vars(sashelp.class(drop=sex));&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Returns:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;PRE&gt;33   %mend list_vars;

INSIDE DOSUBL
GLOBAL VARLIST Name Age Height Weight
LIST_VARS DATASET sashelp.class(drop=sex)
LIST_VARS DSID
LIST_VARS I
LIST_VARS RC
LIST_VARS VARLIST
LIST_VARS VARS

34
35   %put %list_vars(sashelp.class(drop=sex));
AFTER DOSUBL
LIST_VARS DATASET sashelp.class(drop=sex)
LIST_VARS DSID
LIST_VARS I
LIST_VARS RC 0
LIST_VARS VARLIST Name Age Height Weight
LIST_VARS VARS

Name Age Height Weight
&lt;/PRE&gt;
&lt;P&gt;The log is a bit out of order, but you can see that inside the DOSUBL block there is a global macro variable VARLIST,&amp;nbsp; the value of which is returned the local macro variable when DOSUBL completes.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 19 May 2025 20:19:26 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966959#M376248</guid>
      <dc:creator>Quentin</dc:creator>
      <dc:date>2025-05-19T20:19:26Z</dc:date>
    </item>
    <item>
      <title>Re: Retrieve variables from dataset affected by keep/drop within macro</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966960#M376249</link>
      <description>&lt;P&gt;Thanks.&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;%macro list_vars(dataset);
%local rc vars dsid i;
%if %sysfunc(indexc(&amp;amp;dataset,())) %then %do;
  %let rc=%sysfunc(dosubl(%nrstr(
options nonotes;
%let vars=;
%list_vars_step(&amp;amp;dataset,mvar=vars)
)));
%end;
%else %do;
  %let dsid = %sysfunc(open(&amp;amp;dataset));
  %do i=1 %to %sysfunc(attrn(&amp;amp;dsid, nvar));
    %let vars=&amp;amp;vars %sysfunc(varname(&amp;amp;dsid,&amp;amp;i));
  %end;
  %let dsid=%sysfunc(close(&amp;amp;dsid));
%end;
&amp;amp;vars.
%mend list_vars;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Results&lt;/P&gt;
&lt;PRE&gt;199  /* Example */
200  %let varlist=;
201  %put %list_vars(sashelp.class);
Name Sex Age Height Weight
202  %put %list_vars(sashelp.class(drop=sex));
Name Age Height Weight
203  %put %list_vars(sashelp.class(rename=(sex=Gender)));
Name Gender Age Height Weight
&lt;/PRE&gt;</description>
      <pubDate>Mon, 19 May 2025 20:32:53 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Retrieve-variables-from-dataset-affected-by-keep-drop-within/m-p/966960#M376249</guid>
      <dc:creator>Tom</dc:creator>
      <dc:date>2025-05-19T20:32:53Z</dc:date>
    </item>
  </channel>
</rss>

