BookmarkSubscribeRSS Feed
BIGuidance
Fluorite | Level 6


Very often it is useful to have a list of all the variables you have in a certain table. You don’t always want to type them out yourself (hard coding) but instead want an automatically generated list based on what is present in the table.


Think about a regression. You can either type all regressor variables yourself or you load a list into a macro variable and then use that macro variable in the regression equation.

Here is an example of how to make this list by DATA step (though PROC SQL is often easier and preferred over the DATA STEP when it comes to loading values into macro variables).

DATA;

          SET SASHELP.VCOLUMN

(WHERE=(LIBNAME = "libraryoftable" AND MEMNAME = "tablename"));

          CALL SYMPUT(COMPRESS('allvariables'||_N_),name);

          CALL SYMPUT('nbr_of_variables'),_N_);

RUN;

SASHELP.VCOLUMN is a metadata table. It contains data about data. In this case it contains the column-names for every table you have. In order to get the variables you want you will need to select the correct table and this is done by putting a restriction on both  the library and table name (often just table name is enough but if you are in a habit of reusing the same table names over different libraries, you are best to select on both). In the example this is done with the WHERE clause. The MEMNAME (member name) is a variable in the VCOLUMN metadata table which contains all the table names.

Here we upload the contends of the name variable to macro variables by using CALL SYMPUT. A combination of _N_ and a chosen macrovariable name (here 'allvariables') will make the datastep construct our macro variable array.

We also keep the number of variables in a macro variable called 'nbr_of_variables' so we can know at what number the array ends.

In order to check whether your macro variables are created correctly you can use %PUT. This will make SAS put the contents of the variables in the log.

%PUT &allvariables1 &allvariables2 &allvariables3;

%PUT &nbr_of_variables;

Calling the first variable of our array is done by resolving &allvariables1. Calling the last one however is a bit more tricky since it requires us to know how many of these macro variables were actually created. Luckily we kept that information in the macro variable &nbr_of_variables.

In order to get the last macro variable of our array we simply combine the two macro names as so (in this article we do not explain how macro variables resolve, you can find more on this in our library or by following the MACRO courses).

%PUT &&allvariables&nbr_of_variables;

Now you are ready to resolve these macro variables anywhere by building a simple macro loop;

%MACRO printmacroarray;

          %DO i = 1%TO &nbr_of_variables;

                   %PUT &&allvariables&i;

          %END;

%MEND printmacroarray;

%printmacroarray;

13 REPLIES 13
PAzevedo
Fluorite | Level 6

I believe sashelp.vcolumn is a runtime view so using proc contents is a much lighter approach, especially when you have a large database with hundreds or even thousands of tables.

15 DATA _NULL_;

16

17 SET SASHELP.VCOLUMN

18

19 (WHERE=(LIBNAME = "DDS" AND MEMNAME = "FINANCIAL_ACCOUNT"));

20

21 CALL SYMPUT(COMPRESS('allvariables'||_N_),name);

22

23 CALL SYMPUT('nbr_of_variables',_N_);

24

25 RUN;

NOTE: Numeric values have been converted to character values at the places given by: (Line):(Column).

21:48 23:42

NOTE: The query as specified involves ordering by an item that doesn't appear in its SELECT clause.

NOTE: There were 129 observations read from the data set SASHELP.VCOLUMN.

WHERE (LIBNAME='DDS') and (MEMNAME='FINANCIAL_ACCOUNT');

NOTE: DATA statement used (Total process time):

real time 45.30 seconds

cpu time 5.48 seconds

26

27 PROC CONTENTS DATA=DDS.FINANCIAL_ACCOUNT OUT=CONTENTS (KEEP=NAME);

28 RUN;

NOTE: The data set WORK.CONTENTS has 129 observations and 1 variables.

NOTE: Compressing data set WORK.CONTENTS increased size by 100.00 percent.

Compressed is 2 pages; un-compressed would require 1 pages.

NOTE: PROCEDURE CONTENTS used (Total process time):

real time 0.30 seconds

cpu time 0.14 seconds

29

30 DATA _NULL_;

31 SET CONTENTS;

3 The SAS System 09:52 Tuesday, January 31, 2012

32 CALL SYMPUT(COMPRESS('allvariables'||_N_),name);

33 CALL SYMPUT('nbr_of_variables',_N_);

34

35 RUN;

NOTE: Numeric values have been converted to character values at the places given by: (Line):(Column).

32:39 33:34

NOTE: There were 129 observations read from the data set WORK.CONTENTS.

NOTE: DATA statement used (Total process time):

real time 0.00 seconds

cpu time 0.02 seconds

Peter_C
Rhodochrosite | Level 12

PAzevedo

what follows is a demo, showing SASHELP.VCOLUMN being read quickly (even on this 4-year-old laptop)

What helps speed up the pass, is the process = PROC SQL.

SASHELP.VCOLUMN is an SQL VIEW which SQL understands, a whole lot more than a DATA STEP does.

With this understanding, SQL can pass through the WHERE clause to the underlying DICTIONARY.COLUMNS which is "an object that DATA STEP does not understand".

Whatever it is that dynamically fills DICTIONARY.COLUMNS it is able to use the WHERE filter passed by PROC SQL to reduce the metadata being examined to just the column information required. So it has the possibility of being even faster than PROC CONTENTS (which also loads table metadata information).

It is not SASHELP.VCOLUMN which is "slow" but rather, the DATA STEP which has to load all metadata, of all columns, in all tables, in all libraries to pass the "table" to the WHERE condition.

Here is the SASlog demonstrating the speed that PROC SQL reads SASHELP.VCOLUMN for the table MAPS.ASIA

The comparison was run in EG4.2

DATA STEP

2.03secs for data step doing everything

or PROC SQL and DATA STEP

.02 secs in PROC SQL (extract the metadata) followed by

.00 secs in the DATA STEP (reading metadata extracted by PROC SQL)

19 %let lib= MAPS;

20 %let mem= ASIA;

21

22 proc sql;

23 create table cols_&mem as select * from sashelp.vcolumn

24 where LIBNAME = "&LIB" AND MEMNAME = "&MEM"

25 ;

NOTE: Table WORK.COLS_ASIA created, with 8 rows and 18 columns.

26 quit;

NOTE: PROCEDURE SQL used (Total process time):

real time 0.02 seconds

cpu time 0.03 seconds

27

28 proc contents data= &lib..&mem noprint out= conts_&mem;

29 run;

NOTE: The data set WORK.CONTS_ASIA has 8 observations and 40 variables.

NOTE: PROCEDURE CONTENTS used (Total process time):

real time 0.03 seconds

cpu time 0.03 seconds

30

31 DATA _null_ ;

32 SET SASHELP.VCOLUMN

33 (WHERE=(LIBNAME = "&LIB" AND MEMNAME = "&MEM"));

34 CALL SYMPUTX(cats('allvariables',_N_),name);

35 CALL SYMPUTX( 'nbr_of_variables',_N_);

36 RUN;

NOTE: There were 8 observations read from the data set SASHELP.VCOLUMN.

WHERE (LIBNAME='MAPS') and (MEMNAME='ASIA');

NOTE: DATA statement used (Total process time):

real time 2.03 seconds

cpu time 0.67 seconds

37

38 data _null_ ;

39 SET cols_&mem;

40 CALL SYMPUTX(cats('allvariable2',_N_),name);

41 CALL SYMPUTX( 'nbr_of_variable2',_N_);

42 RUN;

NOTE: There were 8 observations read from the data set WORK.COLS_ASIA.

NOTE: DATA statement used (Total process time):

real time 0.00 seconds

cpu time 0.01 seconds

43

44 data _null_ ;

45 SET conts_&mem;

46 CALL SYMPUTX(cats('allvariable3',_N_),name);

47 CALL SYMPUTX( 'nbr_of_variable3',_N_);

48 RUN;

NOTE: There were 8 observations read from the data set WORK.CONTS_ASIA.

NOTE: DATA statement used (Total process time):

real time 0.00 seconds

cpu time 0.00 seconds

The log also provides the "real" runtimes for using PROC CONTENTS and DATA STEP

.03 secs in PROC SQL (extract the metadata) followed by

.00 secs in the DATA STEP (reading metadata extracted by PROC CONTENTS)

The more libraries that are defined, or more tables that are present within those libraries, the more a DATA STEP has to read from SASHELP.VCOLUMN.

My setup has little more than base SAS and SAS/GRAPH. Your's might have more to read.

peterC

FriedEgg
SAS Employee

Here is a timed example using a library connected to an oracle database containing hundreds of tables:

15         libname ort oracle user=user pass=pass path=path;

NOTE: Libref ORT was successfully assigned as follows:

      Engine:        ORACLE

      Physical Name: path

16        

17         proc sql noprint;

18          select name into :allvariables1 separated by ' '

19            from sashelp.vcolumn

20           where libname = 'ORT'

21                 and memname= 'TABLE';

22          %let nbr_of_variables1=&sqlobs;

23         quit;

NOTE: PROCEDURE SQL used (Total process time):

      real time           2.92 seconds

      cpu time            0.01 seconds

     

24        

25         proc sql noprint;

26          select name into :allvariables2 separated by ' '

27            from dictionary.columns

28           where libname = 'ORT'

29                 and memname= 'TABLE';

30          %let nbr_of_variables2=&sqlobs;

31         quit;

NOTE: PROCEDURE SQL used (Total process time):

      real time           2.80 seconds

      cpu time            0.00 seconds

     

32        

33         proc contents data=ort.table noprint out=contents; run;

NOTE: The data set WORK.CONTENTS has 24 observations and 40 variables.

NOTE: PROCEDURE CONTENTS used (Total process time):

      real time           0.00 seconds

      cpu time            0.00 seconds

     

34        

35         data _null_;

36          length slurp $32000;

37          do until(done);

38           set contents end=done;

39           slurp=catx(' ',of slurp name);

40           i+1;

41          end;

42          call symputx('allvariables3',slurp);

43          call symputx('nbr_of_variables3',i);

44         run;

NOTE: There were 24 observations read from the data set WORK.CONTENTS.

NOTE: DATA statement used (Total process time):

      real time           0.00 seconds

      cpu time            0.00 seconds

An additional example showing that sashelp.vcolumn can be accessed quickly…  dictionary.columns is typically faster since it is avoiding the extra view facility, however the difference is often negligible.

17         proc sql noprint;

18          select name into :allvariables1 separated by ' '

19            from sashelp.vcolumn

20           where libname = 'MAPS'

21                 and memname= 'ASIA';

22          %let nbr_of_variables1=&sqlobs;

23         quit;

NOTE: PROCEDURE SQL used (Total process time):

      real time           0.04 seconds

      cpu time            0.01 seconds

     

24        

25         proc sql noprint;

26          select name into :allvariables2 separated by ' '

27            from dictionary.columns

28           where libname = 'MAPS'

29                 and memname= 'ASIA';

30          %let nbr_of_variables2=&sqlobs;

31         quit;

NOTE: PROCEDURE SQL used (Total process time):

      real time           0.00 seconds

      cpu time            0.00 seconds

     

32        

33         proc contents data=maps.asia noprint out=contents; run;

NOTE: The data set WORK.CONTENTS has 8 observations and 40 variables.

NOTE: PROCEDURE CONTENTS used (Total process time):

      real time           0.00 seconds

      cpu time            0.00 seconds

     

34        

35         data _null_;

36          length slurp $32000;

37          do until(done);

38           set contents end=done;

39           slurp=catx(' ',of slurp name);

40           i+1;

41          end;

42          call symputx('allvariables3',slurp);

43          call symputx('nbr_of_variables3',i);

44         run;

NOTE: There were 8 observations read from the data set WORK.CONTENTS.

NOTE: DATA statement used (Total process time):

      real time           0.00 seconds

      cpu time            0.01 seconds

Linlin
Lapis Lazuli | Level 10

Hi,

I think the libname and table name in your code have to be capital letters.

DATA;

          SET SASHELP.VCOLUMN

(WHERE=(LIBNAME = "LIBRARYOFTABLE" AND MEMNAME = "TABLENAME"));

          CALL SYMPUTx(cats('allvariables',_N_),name);

          CALL SYMPUTx('nbr_of_variables',_N_);

RUN;

Haikuo
Onyx | Level 15

I apologize if this is a bit off-topic. There are numerous ways from table variable names to macro variable values, such as dictionary.column (SQL), sashelp.vcolumn (datastep), proc content, even array-vname(). However, are there any  approaches (preferablely non-macro)  to do the reverse conversion? eg. converting macro variable values back to table variable names?

Thanks,

Haikuo

art297
Opal | Level 21

Hai.kuo,

Not sure what you are asking.  If you have a list of space-separated variables names, and they are all either numeric or have already had informats assigned, you can always use the macro variable for text substitution.  e.g.,

data want;

   input &yourMacroVariable.;

   etc.

run;

Haikuo
Onyx | Level 15

Thanks, Art.

Your answer makes me realize that I have asked a wrong question. Rather, it is more of macro question. To simply my scenario, here is an example:

%let m=test1 test2;

%let n=_2012;

data have;

%scan(&m,1)=1

%scan(&m,2)=2;

%scan(&m,1)||&n=999;

run;

I have trouble to make a concatenated variable out of macro variable values.

Thanks  in advance,

Haikuo

Tom
Super User Tom
Super User

You do not need the concatenation operator.

Just code %scan(&m,1)&n .

127  %let m=test1 test2;

128  %let n=_2012;

129

130

131  data have;

132  %scan(&m,1)=1;

133  %scan(&m,2)=2;

134  %scan(&m,1)&n=999;

135   put (_all_) (=);

136  run;

test1=1 test2=2 test1_2012=999

Haikuo
Onyx | Level 15

aha! Many Many Thanks, Tom!!! I knew something wrong with my way of thinking upon macro.

Tom
Super User Tom
Super User

Code generation.  I perfer to use data step with PUT statement to generate code as it is much easier to debug.

For example you could generate ATTRIB statements.

filename code temp;

data _null_;

  file code ;

  set contents ;

  put 'attrib ' name 'length=' @;

  if type=2 then put '$'@;

  put length @;

  if label ne ' ' then put "label='" label +(-1) "'" @;

  put ';';

run;

data new ;

  %inc code;

run;

data_null__
Jade | Level 19

I'm glad you said that Tom as I feel the same, especially if the bulk of the data is already in a SAS data set.  And you can make it look nice with indentation and the rest and even more importantly SAS can correctly associate errors with line numbers unlike with MPRINT.  %INC was all we had back in the day, way back.  Macro language + %INC "cat’s meow"

Many times as in this example LABEL and LENGTH can be printed with LIST put making the program a bit more concise.

17427  filename code temp;

17428  data _null_;

17429     *file code;

17430     set contents;

17431     put +3 'Attrib ' name @;

17432     select(type);

17433        when(1) put length= @;

17434        when(2) put length=dollar8. @;

17435        end;

17436     if not missing(label) then put label=:quote258. @;

17437     put ';';

17438     run;

   Attrib AgeAtDeath LENGTH=8 LABEL="Age at Death" ;

   Attrib AgeAtStart LENGTH=8 LABEL="Age at Start" ;

   Attrib AgeCHDdiag LENGTH=8 LABEL="Age CHD Diagnosed" ;

   Attrib BP_Status LENGTH=$7 LABEL="Blood Pressure Status" ;

   Attrib Chol_Status LENGTH=$10 LABEL="Cholesterol Status" ;

   Attrib Cholesterol LENGTH=8 ;

   Attrib DeathCause LENGTH=$26 LABEL="Cause of Death" ;

   Attrib Diastolic LENGTH=8 ;

   Attrib Height LENGTH=8 ;

   Attrib MRW LENGTH=8 LABEL="Metropolitan Relative Weight" ;

   Attrib Sex LENGTH=$6 ;

   Attrib Smoking LENGTH=8 ;

   Attrib Smoking_Status LENGTH=$17 LABEL="Smoking Status" ;

   Attrib Status LENGTH=$5 ;

   Attrib Systolic LENGTH=8 ;

   Attrib Weight LENGTH=8 ;

   Attrib Weight_Status LENGTH=$11 LABEL="Weight Status" ;

NOTE: There were 17 observations read from the data set WORK.CONTENTS.

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 13 replies
  • 10407 views
  • 1 like
  • 9 in conversation