I created 40 plus tables (using a marco, which I just learned how to do) that I would like to apply the Proc Sort statement to. I want each table sorted by the same variable 'Account_Description' (each table contains this variable).
The table names are June_53410_v1, June_53420_v1, June_53430_v1, etc. Can I employ a macro, and if so, how can I, to mitigate having to write a proc sort statement for each table?
Thanks!
I found this sample code online but I'm not really sure how it works
%Macro sorter(dsn, var);
proc sort data=&dsn.;
by &var.;
run;
%mend;
%sorter(sample_dataset, age);
Macro that will be used (proc sort write to work):
%Macro sorter(lib,dsn, var);
proc sort data=&lib..&dsn. out=&dsn.;
by &var.;
run;
%mend;
Get dictionary of tables that contains in name some chars (its maby “June_” instead “AIR”) :
data sashelp_tables;
set sashelp.vtable;
where LIBNAME="SASHELP" and MEMNAME contains "AIR"
;
run;
Write code to string , and execute it for all tables:
data _NULL_;
length code $ 200;
set sashelp_tables;
code=cat('%sorter(',LIBNAME,',',MEMNAME,',AIR);');
call execute(code);
run;
I appreciate everyone's input-I think I found an answer though using this code:
%macro st (ds);
proc sort data = &ds;
by Account_Description;
run;
%mend;
%st(June_53410_v1);
%st(June_53420_v1);
You can use this solution, where lib is libname, mask_table is mask to table(June_ in your task) and var is variable to sort tables:
%macro sorter(lib,mask_table, var); %macro d;%mend d;
%let table_list = 0;
proc sql noprint;
select strip(libname) || '.' || strip(memname),count(memname)
into: table_list separated by ' '
from dictionary.tables
where libname = UPCASE("&lib.") and memname LIKE UPCASE("&mask_table.")||"%";
quit;
%do i=1 %to %sysfunc(countw(&table_list,%str( )));
%let name&i = %scan(&table_list, &i, %str( ));
proc sort data=&&name&i.;
by &var.;
run;
%end;
%mend sorter;
%sorter(WORK,June,Account_Description);
Related
I'm doing a crash course on SAS macros and I'm stuck at one exercise. I have to create a macro, that will create a proc contents tables for every data set, that contains a keyword. I know how to do that using call execute, but I need this using proc sql and %do loop.
My attempt:
%macro contents(data=&syslast);
proc contents data=&data;
title "&data";
run;
%mend contents;
%macro ContentsAll(keyword);
select libname||'.'||memname
into :dsn1-
from sashelp.vstabvw
where upcase(memname) like %upcase("%quote(%)&&keyword%")
;
quit;
%do i=1 %to &sqlobs;
%contents(data=&&dsn&i);
%end;
%mend ContentsAll;
options mlogic mprint;
%ContentsAll(class);
options nomlogic nomprint;
I know there is some issue with a select statement, but I have no idea how to fix it. And where statement has an unprotected variable (my attempts at fixing it just break the where clause alltogether.
First of all, good job. It's so good that I'm almost sorry you're only missing the Proc SQL Statement :-)
%macro contents(data=&syslast);
proc contents data=&data;
title "&data";
run;
%mend contents;
%macro ContentsAll(keyword);
proc sql noprint;
select libname||'.'||memname
into :dsn1-
from sashelp.vstabvw
where upcase(memname) like %upcase("%quote(%)&&keyword%")
;
quit;
%do i=1 %to &sqlobs;
%contents(data=&&dsn&i);
%end;
%mend ContentsAll;
options mlogic mprint;
%ContentsAll(class);
options nomlogic nomprint;
There is no need to create all of those macro variables. Just keep the list of names in actual data. You can use CALL EXECUTE() to generate the code you want to run for each member in the list.
Note that the variables LIBNAME and MEMNAME will already be in uppercase when pulled from the DICTIONARY.MEMBERS metadata that the view SASHELP.VSTABVW uses. But the user passing in a value for the KEYWORD parameter might not have entered uppercase letters.
%macro ContentsAll(keyword);
data _null_;
set sashelp.vstabvw ;
where memname contains "%qupcase(&keyword)" ;
call execute(cats('%nrstr(%contents)(data=',libname,'.',memname,')'));
run;
%mend ContentsAll;
I have multiple tables in a library call snap1:
cust1, cust2, cust3, etc
I want to generate a loop that gets the records' count of the same column in each of these tables and then insert the results into a different table.
My desired output is:
Table Count
cust1 5,000
cust2 5,555
cust3 6,000
I'm trying this but its not working:
%macro sqlloop(data, byvar);
proc sql noprint;
select &byvar.into:_values SEPARATED by '_'
from %data.;
quit;
data_&values.;
set &data;
select (%byvar);
%do i=1 %to %sysfunc(count(_&_values.,_));
%let var = %sysfunc(scan(_&_values.,&i.));
output &var.;
%end;
end;
run;
%mend;
%sqlloop(data=libsnap, byvar=membername);
First off, if you just want the number of observations, you can get that trivially from dictionary.tables or sashelp.vtable without any loops.
proc sql;
select memname, nlobs
from dictionary.tables
where libname='SNAP1';
quit;
This is fine to retrieve number of rows if you haven't done anything that would cause the number of logical observations to differ - usually a delete in proc sql.
Second, if you're interested in the number of valid responses, there are easier non-loopy ways too.
For example, given whatever query that you can write determining your table names, we can just put them all in a set statement and count in a simple data step.
%let varname=mycol; *the column you are counting;
%let libname=snap1;
proc sql;
select cats("&libname..",memname)
into :tables separated by ' '
from dictionary.tables
where libname=upcase("&libname.");
quit;
data counts;
set &tables. indsname=ds_name end=eof; *9.3 or later;
retain count dataset_name;
if _n_=1 then count=0;
if ds_name ne lag(ds_name) and _n_ ne 1 then do;
output;
count=0;
end;
dataset_name=ds_name;
count = count + ifn(&varname.,1,1,0); *true, false, missing; *false is 0 only;
if eof then output;
keep count dataset_name;
run;
Macros are rarely needed for this sort of thing, and macro loops like you're writing even less so.
If you did want to write a macro, the easier way to do it is:
Write code to do it once, for one dataset
Wrap that in a macro that takes a parameter (dataset name)
Create macro calls for that macro as needed
That way you don't have to deal with %scan and troubleshooting macro code that's hard to debug. You write something that works once, then just call it several times.
proc sql;
select cats('%mymacro(name=',"&libname..",memname,')')
into :macrocalls separated by ' '
from dictionary.tables
where libname=upcase("&libname.");
quit;
¯ocalls.;
Assuming you have a macro, %mymacro, which does whatever counting you want for one dataset.
* Updated *
In the future, please post the log so we can see what is specifically not working. I can see some issues in your code, particularly where your macro variables are being declared, and a select statement that is not doing anything. Here is an alternative process to achieve your goal:
Step 1: Read all of the customer datasets in the snap1 library into a macro variable:
proc sql noprint;
select memname
into :total_cust separated by ' '
from sashelp.vmember
where upcase(memname) LIKE 'CUST%'
AND upcase(libname) = 'SNAP1';
quit;
Step 2: Count the total number of obs in each data set, output to permanent table:
%macro count_obs;
%do i = 1 %to %sysfunc(countw(&total_cust) );
%let dsname = %scan(&total_cust, &i);
%let dsid=%sysfunc(open(&dsname) );
%let nobs=%sysfunc(attrn(&dsid,nobs) );
%let rc=%sysfunc(close(&dsid) );
data _total_obs;
length Member_Name $15.;
Member_Name = "&dsname";
Total_Obs = &nobs;
format Total_Obs comma8.;
run;
proc append base=Total_Obs
data=_total_obs;
run;
%end;
proc datasets lib=work nolist;
delete _total_obs;
quit;
%mend;
%count_obs;
You will need to delete the permanent table Total_Obs if it already exists, but you can add code to handle that if you wish.
If you want to get the total number of non-missing observations for a particular column, do the same code as above, but delete the 3 %let statements below %let dsname = and replace the data step with:
data _total_obs;
length Member_Name $7.;
set snap1.&dsname end=eof;
retain Member_Name "&dsname";
if(NOT missing(var) ) then Total_Obs+1;
if(eof);
format Total_Obs comma8.;
run;
(Update: Fixed %do loop in step 2)
I have several databases, one per geographical variables, that I want to append in the end. I am doing some data steps on them. As I have large databases, I select only the variables I need when I first call each table. But on tables in which one variable always equals 0, the variable is not in the table.
So when I select my (keep=var) in a for loop, it works fine if the variable exists, but it produces an error in the other case, so that these tables are ignored.
%do i=1 to 10 ;
data temp;
set area_i(keep= var1 var2);
run;
proc append base=want data=temp force;
run;
%end;
Is there a simple way to tackle that ?
In fact I have found a solution : the DKRICOND (or DKROCOND) options specify the level of error detection to report when a variable is missing from respectively an input (or output) data set during the processing of a DROP=, KEEP=, or RENAME= data set option.
The options are DKRICOND=ERROR | WARN | WARNING | NOWARN | NOWARNING, so you just wave to set
dkricond=warn
/*your program, in my case :*/
%do i=1 to 10 ;
data temp;
set area_i(keep= var1 var2);
run;
proc append base=want data=temp force;
run;
%end;
dkricond=error /* the standard value, probably better to set it back after/ */
How about just adding it to the table if it doesn't already exist?
/*look at dictionary.columns to see if the column already exists*/
proc sql;
select name into :flag separated by ' ' from dictionary.columns where libname = 'WORK' and memname = 'AREA_I' and name = 'VAR1';
run;
/*if it doesn't, then created it as empty*/
%if &flag. ne VAR1 %then %do;
data area_i;
set area_i;
call missing(var1);
run;
%end;
I am working on SAS in UNIX env and I want to view only the column name of a dataset. I have tried proc contents and proc print but both of them list a lot of other irrevelant information that I do not want as it fills up my putty screen and the information ultimately is lost.
I also tried to get this thing frm the sas metadata but that is not working either.
I tried :
2? proc sql;
select *
from dictionary.tables
where libname='test' and memname='sweden_elig_file_jul';
quit;
5?
NOTE: No rows were selected.
6?
NOTE: PROCEDURE SQL used (Total process time):
real time 0.27 seconds
cpu time 0.11 seconds
You're using the wrong dictionary table to get column names...
proc sql ;
select name
from dictionary.columns
where memname = 'mydata'
;
quit ;
Or using PROC CONTENTS
proc contents data=mydata out=meta (keep=NAME) ;
run ;
proc print data=meta ; run ;
Here's one I've used before to get a list of columns with a little bit more information, you can add the keep option as in the previous answer. This just demonstrates how to create a connection to the metadata server, in case that is useful to anyone viewing this post.
libname fetchlib meta
library="libraryName" metaserver="metaDataServerAddress"
password="yourPassword" port=1234
repname="yourRepositoryName" user="yourUserName";
proc contents data=fetchlib.YouDataSetName
memtype=DATA
out=outputDataSet
nodetails
noprint;
run;
For a pure macro approach, try the following:
%macro mf_getvarlist(libds
,dlm=%str( )
)/*/STORE SOURCE*/;
/* declare local vars */
%local outvar dsid nvars x rc dlm;
/* open dataset in macro */
%let dsid=%sysfunc(open(&libds));
%if &dsid %then %do;
%let nvars=%sysfunc(attrn(&dsid,NVARS));
%if &nvars>0 %then %do;
/* add first dataset variable to global macro variable */
%let outvar=%sysfunc(varname(&dsid,1));
/* add remaining variables with supplied delimeter */
%do x=2 %to &nvars;
%let outvar=&outvar.&dlm%sysfunc(varname(&dsid,&x));
%end;
%End;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put unable to open &libds (rc=&dsid);
%let rc=%sysfunc(close(&dsid));
%end;
&outvar
%mend;
Usage:
%put List of Variables=%mf_getvarlist(sashelp.class);
Returns:
List of Variables=Name Sex Age Height Weight
source: https://github.com/sasjs/core/blob/main/base/mf_getvarlist.sas
proc sql;
select *
from dictionary.tables
where libname="TEST" and memname="SWEDEN_ELIG_FILE_JUL";
quit;
How do you read multiple specific datasets and append to one big dataset?
For example I within a library I have 100s of datasets but I only want to append the datasets that have _du1, _du2
The format and column names are the same
My stab of it doesnt work:
PROC SQL NOPRINT;
SELECT memname INTO :tab1-:tab103 FROM sashelp.vtable
where memname like '_DU%';
SELECT count(*) INTO :obs FROM sashelp.vtable
where memname like '_DU%';
QUIT;
%macro rubber;
%do i=1 %to i=&obs;
proc append base=tot_comb data=&&tab&i force;
run;
%end;
%mend;
%rubber;
PROC APPEND may not actually be faster in this case, or at least not faster by enough to justify doing it, than just writing a datastep.
data tot_comb;
set work._DU:; *or your libname;
run;
This will work if you are on SAS 9.2 or later. If you're on 9.1 or earlier, you'll need to do one proc sql step, like
proc sql;
select memname into :namelist separated by ' '
from dictionary.columns
where libname='WORK' /* or your libname */
and memname eqt '_DU';
quit;
*eqt is like starts with;
data tot_comb;
set &namelist;
run;
That only requires one pass to write, and I'm not sure it will be much slower than so many calls to PROC APPEND.
Here is some code that will get you all the data set names from a given library with some characteristics (starts with _DU). You could use the final macro in a variety of ways to append data sets.
Data _DU1;
var="One";
Run;
Data _DU2;
var="Two";
Run;
PROC SQL;
create table main as
SELECT *
FROM DICTIONARY.COLUMNS
WHERE UPCASE(LIBNAME)="WORK" AND
UPCASE(MEMNAME) like '_DU%';
Select memname
into :dsn separated by ' '
from main;
QUIT;
%Put &dsn;
EDIT (according to your comment)
I added some UPCASE statements and used your count macro var for the number of tab macros
Narrowing your where statement should make your code more efficient
Try this (some of the code is untested):
PROC SQL NOPRINT;
SELECT count(*)
INTO :obs
FROM sashelp.vtable
where UPCASE(LIBNAME)="<YOUR LIB IN UPCASE>" AND
upcase(memname) like '_DU%';
%Let obs=&obs;
SELECT memname
INTO :tab1-:tab&obs
FROM sashelp.vtable
where UPCASE(LIBNAME)="<YOUR LIB IN UPCASE>" AND
upcase(memname) like '_DU%';
QUIT;
%macro rubber;
%do i=1 %to &obs;
proc append base=tot_comb data=&&tab&i force; run;
%end;
%mend;
%rubber;