SAS exclude variables from selection in dictionary columns - dictionary

I need to do a loop over a list of variables but excluding some of those.
I wanted to add a prefix to each variable except for those.
I wrote a macro:
%macro addprefijo(tabla);
proc contents data = labo2.&tabla.;
title 'before renaming'; run;
proc sql;
select nvar into :num_vars
from dictionary.tables
where libname='LABO2' and memname="&tabla";
%put 'num_vars' &num_vars;
select distinct(name) into :var1-:var%trim(%left(&num_vars))
from dictionary.columns
where libname='LABO2' and memname="&tabla" /*and name not in ('cid', 'COUNTY', 'ESTADO') */;
quit;
proc datasets library=LABO2;
modify &tabla;
rename
%do i=1 %to &num_vars.;
&&var&i = &tabla._&&var&i.
%end;
;
quit;
run;
proc contents data=LABO2.&tabla.;
title' after renaming';
run;
%mend;
%addprefijo(A_CLI);
I tried what is commented but crashes and with out it adds the prefix to all the variables. Those 3 variables are not in all the tables.
How can I solve it?
Thanks

The following should work. Using proc contents out = rather than dictionary tables. Also using the sql separated by syntax to create a space separated list of variables rather than indervidual indexed variables.
%macro addprefijo(tabla);
proc contents
data = labo2.&tabla.
out = _a_contents
noprint;
run;
proc sql noprint;
select NAME
into :vars separated by " "
from _a_contents
where NAME not in ('cid', 'COUNTY', 'ESTADO');
quit;
proc datasets library = labo2;
modify &tabla.;
rename
%do i = 1 %to %sysfunc(countw(&vars., %str( )));
%let var = %scan(&vars., &i., %str( ));
&var. = &tabla._&var.
%end;;
quit;
%mend;
%addprefijo(A_CLI);

Related

R: export multiple workbook excel from a dataset by a variable

i have a statistical df organised by years, for example 5 and i'll would like to export my table in 5 different Excel Workbook, how can i do? First i used SAS and i did it with macros like this
%let elenco = gen feb mar apr giu;
%macro export;
%local i;
%let i = 0;
%do %until (%scan(&elenco,&i+1) = );
%let i = %eval(&i+1);
%let ele=%scan(&elenco,&i);
data month_&i;
set tot;
where month="&ele";
run;
proc export data = month_&i
outfile = "C:\prova_&i.xls"
dbms = excelcs replace;
sheet="month_&i";
run;
%end;
Thanks in advance
Ods excel does not work well with big datasets. But it could be worth a try.
* Sort by your sheet-variable ;
proc sort data=sashelp.class out=work.class;
by age;
run;
ods excel
file='c:\temp\mult_sheet.xlsx'
options(sheet_interval='output' sheet_name='#byval1');
proc print data=work.class label noobs;
by age;
run;
ods excel close;

SAS: dataset name Remove the date and replace with the current date

I currently have a macro which moves my dataset from one branch to another. In each movement, the date is updated. However, I am having trouble with removing the old date autonomously before inputting the new date, any advice?
The code is:
%Macro Merge_Branch(Branch = , Filename = , Library = );
%Let Timestamp = %sysfunc(putn(%sysfunc(date()),yymmddn8.));
%if &Branch. = Latest_Commit %then %do;
Data LC._&Timestamp._&Filename.;
set &Library..&Filename.;
run;
%end;
%else %if &Branch. = Staging %then %do;
%Let Filename = _20180909_Financial_Input;
%Let Filename_temp = %scan(&Filename.,2,'_');
%Let Date_String = %scan(&Filename.,1,'_');
/* this is the section where I get stuck the dash is a subtraction i.e. I want to remove the date and just have the string*/
%Let Filename_Stg = &Filename_temp - &Date_String;
Data Stg._&Timestamp._&Filename_Stg.;
set LC.&Filename.;
run;
%end;
%mend;
Input data can be made like this
data LC._20180909_Financial_Input;
var var1;
datalines;
1
1
1
1
;
run;
%Macro Merge_Branch(Branch = Staging , Filename = Financial_Input, Library = LC );
/*Note, in this macro the library is not important because it will always move from LC to STG*/
Your new file name is resolving to this.
Data Stg._20181009_Financial - 20180909;
That is not a SASNAME.

refer to stored proc output param in macro?

Is there a way to refer to an output parameter of a stored procedure in a macro?
My stored procedure is:
CREATE PROCEDURE db.ssis_load_nextID
(IN tbl VARCHAR(30), OUT nextID SMALLINT )
BEGIN
DECLARE maxID SMALLINT;
SELECT MAX(loadID) INTO maxID
FROM db.SSIS_Load
WHERE TABLENAME = tbl
GROUP BY TABLENAME;
IF maxID IS NULL THEN
SET nextID = 1;
ELSE
SET nextID = maxID + 1;
END if;
END;
I want to refer to this result in a macro like:
CREATE MACRO db.tbSTG_m AS (
INSERT INTO db.tbProd (ID1, ID2, f1, f2, ..., fn, loadID)
SELECT ID1, ID2, f1, f2,..., fn,
CALL db.ssis_Load_nextID('tbProd',nextID)
FROM db.tbstg
; );
because running CALL db.ssis_Load_nextID('tbProd',nextID) returns the result I want in the first (only) row of the first (only) column.
I tried storing the result in a variable in the macro, but apparently, that's unsupported.
Also, I'd like to start with an empty SSIS_load table, so it creates the first row when the first table is loaded, instead of pre-populating the load table before the automated load process starts.
All help appreciated,
-Beth
fyi, We got it to work by removing the 'group by tablename' clause and embedding the sp in the macro:
CREATE MACRO db.tbSTG_m AS (
INSERT INTO db.tbProd
SELECT ID1, ID2, f1, f2, ..., fn (
SELECT ZEROIFNULL(MAX(loadID))+1
FROM db.ssis_load
WHERE TABLENAME = 'tbStg') mx
FROM db.tbSTG;
);
You can't use a stored proc for that (you would have to use a UDF not a procedure)
however you can do it in your macro
syntax may not be 100% correct.. working from memory but should get you close
I am assuming tbl is a parameter passed in correct?
basically you join to the id table and use that in your insert...
then you update the id table with the maximum freshly inserted ids
CREATE MACRO db.tbSTG_m AS (
INSERT INTO db.tbProd (ID1, ID2, f1, f2, ..., fn, loadID)
SELECT ID1, ID2, f1, f2,..., fn, MAXloadID + SUM(1) OVER(ROWS UNBOUNDED PRECEDING)
FROM db.tbstg
cross join (SELECT MAX(loadID) as MAXloadID
FROM db.SSIS_Load
WHERE TABLENAME = tbl
GROUP BY TABLENAME) as IDGEN
;
update db.SSIS_Load from (select MAX(loadID) as MAXloadID from tbl) as upid
set loadID = upid.MAXloadID
where db.SSIS_Load.TABLENAME = tbl
);

Recursively add to a data table in SAS

I am new to SAS. I need to do x-iterations to populate my dataset called MYRS.
Each iteration needs to JOIN TABLE1 with (TABLE2+ MYRS) MINUS the records which are already in MYRS table.
Then, I need to update MYRS table with additional matches. The GOAL is to track a chain of emails.
MYRS is essentially a copy of TABLE1 and contains matching records. Kind of tricky. (simplified schema). Table1 Can have DUPS.
For example
TABLE1:
ID | EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4|
1 | A | s | d | F
2 | g | F | j | L
3 | z | x | L | v
4 | z | x | L | v
2 | g | F | j | L
TABLE2:
EMAIL
A
MYRS (starts as empty dataset)
EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4
Logic: TABLE1 has email that matches email in TABLE2. Therefore this record need to show up. Other records don't match anything in TABLE2. But because Record1 and Record2 share the same ALTERNATIVE email F, Record2 also need to be shown. But because Record2 and Record3 share same alternative email L, Record3 also needs to be shown. And so fourth...
proc sql;
SELECT TABLE1.id,
TABLE1.email1,
TABLE1.email2,
TABLE1.email3,
TABLE1.email4
FROM TABLE1
INNER JOIN (
SELECT EMAIL
FROM TABLE2
UNION
SELECT EMAIL1 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL2 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL3 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL4 AS EMAIL
FROM MYRS
)
ON EMAIL=EMAIL1 OR EMAIL=EMAIL2 OR EMAIL=EMAIL3 OR EMAIL=EMAIL4
WHERE TABLE1.id NOT IN (
SELECT DISTINCT ID
FROM MYRS
)
quit;
How can I create the following logic:
Wrap this into some sort of function
Before sql execution, count amount of records in MYDS and SAVE the count
Execute SQL and update MYDS
Count amount of records in MYDS
If MYDS count did not change, stop execution
Else, goto #3
I am very new to SAS (3 days to be exact) and trying to put everything together. (I would use the logic above if I was to do that in Java)
Here is a macro approach, it mostly follows your logic but transforms your data first and the input/output is a list of IDs (you can easily get to and from emails with this).
This code will probably introduce quite a few SAS features that you are unfamiliar with, but the comments and explanations below should help . If any of it is still unclear take a look at the links or add a comment.
It expects input data:
inData: Your TABLE1 with ID and EMAIL* variables
matched: An initial list of known wanted IDs
It returns:
matched: An updated list of wanted IDs
/* Wrap the processing in a macro so that we can use a %do loop */
%macro looper(maxIter = 5);
/* Put all the emails in one column to make comparison simpler */
proc transpose data = inData out = trans (rename = (col1 = email));
by ID;
var email:;
run;
/* Initialise the counts for the %where condition */
%let _nMatched = 0;
%let nMatched = 1;
%let i = 0;
/* Loop until no new IDs are added (or maximum number of iterations) */
%do %while(&_nMatched. < &nMatched. and &i < &maxIter.);
%let _nMatched = &nMatched.;
%let i = %eval(&i. + 1);
%put NOTE: Loop &i.: &nMatched. matched.;
/* Move matches to a temporary table */
proc datasets library = work nolist nowarn;
delete _matched;
change matched = _matched;
quit;
/* Get new matched IDs */
proc sql noprint;
create table matched as
select distinct c.ID
from _matched as a
left join trans as b
on a.ID = b.ID
left join trans as c
on b.email = c.email;
/* Get new count */
select count(*) into :nMatched from matched;
quit;
%end;
%mend looper;
%looper(maxIter = 10);
The interesting bits are:
proc transpose: Converts the input into a deep table so that all the email addresses are in one variable, this makes writing the email comparison logic simpler (less repetition needed) and puts the data in a format that will make it easier for you to clean the email addresses if necessary (think upcase(), strip(), etc.).
%macro %mend: The statements used to define a macro. This is necessary as you cannot use macro logic or loops in open code. I've also added an argument so you can see how that works.
%let and select into :: Two ways to create macro variables. Macro variables are referenced with the prefix & and are used to insert text into the SAS program before it is executed.
%do %while() %end: One of the ways to perform a loop within a macro. The code within will be run repeatedly until the condition evaluates to false.
proc datasets: A procedure for performing admin tasks on datasets and libraries. Used here to delete and rename temporary tables.

Overlay SAS GMAP Choropleths

Reformulation: I want to overlay the following two Graphs:
data mystates;
set maps.states;
myvar = state<30;
run;
pattern1 c=black v=m3n0;
pattern2 c=black v=m3n90;
%let except = (where=(state not in (72,2,15)));
proc gmap map=mystates &except. data=mystates &except.;
id state;
choro myvar;
run;
quit;
And
goptions reset=all;
%let no = 48;
proc gmap map=maps.counties (where =(state=&no.)) data=maps.counties (where =(state=&no.));
id county;
choro county;
run;
quit;
As the granularity is different I cannot simply use 2 choro statements in teh proc gmap: Note how the order of the two choro statements matters. One always overdraws the other.
data mytry;
set mystates &except. maps.county (where =(state=&no.));
run;
pattern1 c=black v=m3n0;
pattern2 c=black v=m3n90;
proc gmap map=mytry data=mytry all;
id state county;
choro myvar;
choro county;
run;
quit;
How can I display both the lines and the colors at the same time?

Resources