SAS macro to resolve in proc sql into statement - sas

Can anyone help me with the syntax error here.
Y is dataset which contain some value let's say 1,2,3,4 (Actually it contains many records)
/*This is working fine*/
proc sql;
select count(*) into:m from y;
select x into:a1 - :a4 from y;
quit;
%put &m &a1 &a2 &a3 &a4;
/*When i am trying to create a macro which will have a1 to a4 values, it's giving me error. below is my approach*/
proc sql;
select count(*) into:m from y;
select x into:a1 - :a||trim(left(&m.)) from y;
quit;
%put &m &a1 &a2 &a3 &a4;
Please can someone help me with this, explain me the reason for error.

You don't need to tell how many anymore. SQL will create just enough variables.
data y;
do x = 1 to 4;
output;
end;
run;
proc sql;
select count(*) into:m from y;
select x into:a1- from y;
quit;
%put &m &a1 &a2 &a3 &a4;
This is valid at least as of 9.3.

The syntax error is probably caused by not understanding how the macro processor works. It is just a tool for generating text. The generated text needs to be valid SAS code for it to execute. So trying to write something like:
into :a1 - :a||trim(left(&m.))
will not work. The only macro trigger there is the reference to the M macro variable. So that would evaluate to:
into :a1 - :a||trim(left( 4))
but the INTO operator just wants the name of the upperbound for the macro variable name list there. It cannot handle the || concatenation operator or call functions like TRIM() or LEFT().
Fortunately you do not need these since the INTO clause of PROC SQL is smart enough to only generate as many macro variables as you need. If you are using a current version of SAS you can leave the upperbound empty.
into :a1 -
or if you are running an old version you just use an upperbound that is larger than any expected number of observations.
into :a1-:a999999
Also you do not need to run the query twice to find out how many records SQL found. It will set the count into the automatic macro variable SQLOBS. So your query becomes:
proc sql noprint;
select x into :a1- from y;
%let m=&sqlobs;
quit;

While DN is correct in the other answer that you really don't have to bother with this anymore, I thought it useful to show you the error in your attempt - which was not an unreasonable approach.
You can't use trim the way you did. That's going to operate on character expressions, not on program code - which a macro variable is producing (program code). You've got a few options for how to do this.
First, there is the %trim autocall macro. That's how you'd do what you were trying to do directly.
proc sql;
select count(*) into :ccount trimmed from sashelp.class;
select name into :a1 - :a%trim(&ccount.) from sashelp.class;
quit;
%put &ccount &a1 &a2 &a3 &a4;
Second, SQL will actually do this for you, in two possible options.
trimmed keyword
separated by
The trimmed keyword as part of the into clause, or using separated by even when there's only one resulting value, will cause a trimmed result (so, left-aligned with no spaces following or preceding) to be stored in the destination macrovariable. (Why the defaults are different for separated by vs. not is one of the weird things in SAS related to not breaking code that works, even when it's a silly result.)
proc sql;
select count(*) into :ccount trimmed from sashelp.class;
select name into :a1 - :a&ccount. from sashelp.class;
quit;
%put &ccount &a1 &a2 &a3 &a4;

Related

SAS macro parameter that is a list

I am trying to create a macro where one of the parameters is a list. My macro includes a proc sql with a where statement that has something like this:
Where Name in ('sarah','ben','adam')
I tried doing something like this:
%MACRO DATA_PULL (name=);
PROC SQL;
SELECT
FROM
Where Name in &name
;
QUIT;
%MEND DATA_PULL;
%DATA_PULL (Name=('sarah','ben','adam'))
but it doesn't work :(
any help appreciated
The SAS in operator does not require commas. This is valid syntax:
where Name in ('sarah' 'ben' 'adam')
so you could have macro invocation with
, names = ('sarah' 'ben' 'adam')
You can also pass commas in a macro parameter by properly quoting the value or value portion. In this case %str can be used.
, names = (%str('sarah','ben','adam'))
If you place the %str outside the in list parenthesis you may also want to escape the parenthesis within the %str
Some example invocations
%macro x(names=);
proc sql;
create table want as
select * from sashelp.class
where name in &names
;
%mend;
%x(names=('Jane' 'James'))
%x(names=(%str('Jane', 'James')))
%x(names=%str(%('Jane', 'James'%)))
you need to using macro quoting functions.
%MACRO DATA_PULL (name=);
PROC SQL;
SELECT *
FROM sashelp.class
Where Name in &name
;
QUIT;
%MEND DATA_PULL;
%DATA_PULL (Name = %str(('Alfred', 'Alice', 'Barbara')))
Trying to put commas in the value of the macro variable is the issue since comma is used to mark the transition between parameter values in the macro call.
What you posted is actually one way to allow the inclusion of commas in the value of a macro parameter. By enclosing the value inside or () the SAS compiler will know that the commas do not mark the start of new parameter values. If you fix your macro so that it generates a valid SELECT statement then it works.
%macro data_pull (name=);
proc sql;
select * from sashelp.class where name in &name;
quit;
%mend data_pull;
%data_pull(name=('Alfred','Alice','ben','adam'))
But the real solution is even easier. Just do not include the commas in the value to begin with. The IN operator does not need them. Then you can add the () in the macro code.
%macro data_pull (name=);
proc sql;
select * from sashelp.class where name in (&name);
quit;
%mend data_pull;
%data_pull(name='Alfred' 'Alice' 'ben' 'adam')
Or you can make your macro a little smarter and then the user can either include the () or not when calling the macro.
%macro data_pull (name=);
proc sql;
select * from sashelp.class
where name in (%scan(&name,1,(),q));
quit;
%mend data_pull;
You can use the SYSPBUFF automatic macro variable, which contains the parameter values you supplied to the macro, including the parentheses and commas. The PARMBUFF option allows one to build a macro to deal with a varying number of parameters.
%macro data_pull / parmbuff;
proc sql;
select *
from sashelp.class
where name in &syspbuff.;
quit;
%mend data_pull;
%data_pull('Alfred','Alice','ben','adam')
In this example, the SYSPBUFF variable is ('Alfred','Alice','ben','adam'), which fits nicely for your SQL query.

I want to convert this code from SAS to ECL(HPCC)

Can Somebody please explain me the meaning &, && , % in this SAS code.
I want to convert this code to ECL(HPCC). Thanks
PROC SQL;
create table &RD (compress=binary) as
select a.*,b.Staff_tag2
from &RD a left join (select DISTINCT ucic_id , 1 as Staff_TAG2
from BAL.BAL_&DDMMYYYY1 where prod_code = '102')b
on a.ucic_id=b.ucic_id;
QUIT;
%macro rd();
%do i=1 %to 12;
proc sql;
create table rd.RD_Closed_&&ddmmyyyy&i (compress=binary) as
select V_F_ACCT_ACCT_NUMBER as acct_no,
V_F_ACCT_FIC_CUSTOMER_REF_CODE as ucic_id
from edw.fct_all_accounts_summary
where business_dt eq &&yyyymmdd&i and V_F_ACCT_SOURCE_SYSTEM = 'FINWARERD' AND F_F_ACCT_CLOSED_IND eq 'Y';
quit;
%end;
%mend;
option mprint;
& and && is the beginning of macro variables. They resolve to a value. The value they resolve to depends how they are defined. An example could be:
%let var=test;
in this case &var resolves to test. But there are other ways to initiate macro variables. The %-sign pretty much means that it belongs to the macro languages. There is much to say about SAS macros but i am not sure exactly how much info your after.

sas add prefix to column names

I need to add a prefix to certain column names in a table. The names at the moment range from _15 to _49 and I would simply like to add the prefix N to give N_15,...,N_49 etc.
I tried the following:
proc sql noprint;
select cats(name,'=','N',name)
into :prefixlist
separated by ' '
from dictionary.columns
where libname = 'WORK' and memname = 'Freq_nais_2006_2010';
quit;
However this does nothing as I just get the message no rows were selected in the log output. What must I change?
Your particular issue is that the WHERE clause is not being fulfilled by any rows, likely because of this: and memname = 'Freq_nais_2006_2010'. Member names are typically capitalized internally in SAS, even if they're not capitalized in your code.
Otherwise your code looks fine, and you should be able to use that &prefixlist. in a PROC DATASETS or data step rename statement. I generally suggest the PROC SQL method as it's easier to customize to specify the variables you want to rename, but of course if you're renaming all of the variables in the dataset the macro works as well.
You're very close:
proc sql noprint;
select cats(name,'=','N',name)
into :prefixlist
separated by ' '
from dictionary.columns
where libname = 'WORK' and memname = 'FREQ_NAIS_2006_2010'
/* and substr(NAME,1,1) = '_' - can add condition on column name pattern */;
quit;
proc datasets lib=WORK nolist nodetails;
modify FREQ_NAIS_2006_2010;
rename
&prefixlist
;
quit;
Changed separator to space for use in PROC DATASETS; MODIFY ... RENAME ... statement.
Side note: the datastep variant answers rewrite the dataset completly, which is ineffective and dangerous for real world usage (big tables), also much less clear on what you're doing.
I managed to find the following code from the sas website (http://support.sas.com/kb/37/433.html):
%macro vars(dsn,chr,out);
%let dsid=%sysfunc(open(&dsn));
%let n=%sysfunc(attrn(&dsid,nvars));
data &out;
set &dsn(rename=(
%do i = 2 %to &n;
%let var=%sysfunc(varname(&dsid,&i));
&var=&chr&var
%end;));
%let rc=%sysfunc(close(&dsid));
run;
%mend vars;
%vars(Freq_nais_2006_2010,N,Freq_nais_2006_2010);
You can just list them in the rename statement in your code, no need for macros or anything else. Though the best idea is avoid it in the first place if you can. See the rename statement below.
data test;
array test(20) _1-_20;
do i=1 to 20;
test(i)=rand('normal', 20);
end;
run;
data test2;
set test;
rename _1-_20 = n_1-n_20;
run;

macro to transpose and add a prefix after ordering columns' names

I need to run a macro that does a transpose for many variables (and creates a table for each one), orders the columns names, which are numeric, but also adds as a prefix the variable's name (which is a string).
I have a macro in SAS to perform a transpose with different variables as var in the transpose. The code is:
%macro transponer(var);
proc transpose data=labo2.A_svm_200711_200806
out=labo2.D_tr_&var.0;
var &var;
id mes;
by cid;
run;
/*......more code.....*/
select cats(name, '=', &var, name)
into :prefijolista
separated by ' '
from dictionary.columns
where libname='LABO2' and memname= cats('D_TR_',upcase(&var))
and name like '_20%';
quit;
%put &prefijolista;
%mend;
Since mes is numeric I wanted to order the variable, that's why I didn't introduce the "prefix &var" in the proc transpose but instead I did it after the retain (that was useful to order the columns).
The problem starts when I try to introduce the prefix (after the ordering).
Since one of the variables' name is for example "monto", I get the following error (because it is the var variable in the transpose and it's not a column name in the transposed table):
The following columns were not found in the contributing tables:
monto.
My next step would be:
proc datasets library=labo2;
modify D_tr_&var.0;
rename &prefijolista;
quit;
But I cant do it untill I get the previous one done.
So I don't know how to order the columns after the transpose and also add the prefix.
How can I solve this?
Thanks!
You need to rename the columns using something like PROC DATASETS.
proc datasets lib=work nolist;
modify myDataSet;
rename old_col_name = new_col_name;
run;
quit;
A documentation example is available in the Base SAS guide under the doc for PROC DATASETS. It is available online at: http://support.sas.com/documentation/cdl/en/proc/67327/HTML/default/viewer.htm#n0mfav25learpan1lerk79jsp30n.htm
The problem was that &var inside the cats function inside a macro hast to use
" "
Also you could use
sysfunc(cats(D_TR, &a)
So finally the code will remain like:
%let a = %upcase(&var);
%put &a;
%let b=%sysfunc(cats(D_TR_,&a));
%put &b;
proc sql;
select cats(name, '=', "&var" , name)
into :prefijolista
separated by ' '
from dictionary.columns
where libname='LABO2' and memname= "&b"
and name like '_20%';
quit;
%put &prefijolista;
%put "&b";
PROC datasets library=LABO2;
modify &b;
rename &prefijolista;
quit;
%put "ult" &b;
Not very straightforward, but worked. :)

SAS sum variables using name after a proc transpose

I have a table with postings by category (a number) that I transposed. I got a table with each column name as _number for example _16, _881, _853 etc. (they aren't in order).
I need to do the sum of all of them in a proc sql, but I don't want to create the variable in a data step, and I don't want to write all of the columns names either . I tried this but doesn't work:
proc sql;
select sum(_815-_16) as nnl
from craw.xxxx;
quit;
I tried going to the first number to the last and also from the number corresponding to the first place to the one corresponding to the last place. Gives me a number that it's not correct.
Any ideas?
Thanks!
You can't use variable lists in SQL, so _: and var1-var6 and var1--var8 don't work.
The easiest way to do this is a data step view.
proc sort data=sashelp.class out=class;
by sex;
run;
*Make transposed dataset with similar looking names;
proc transpose data=class out=transposed;
by sex;
id height;
var height;
run;
*Make view;
data transpose_forsql/view=transpose_forsql;
set transposed;
sumvar = sum(of _:); *I confirmed this does not include _N_ for some reason - not sure why!;
run;
proc sql;
select sum(sumvar) from transpose_Forsql;
quit;
I have no documentation to support this but from my experience, I believe SAS will assume that any sum() statement in SQL is the sql-aggregate statement, unless it has reason to believe otherwise.
The only way I can see for SAS to differentiate between the two is by the way arguments are passed into it. In the below example you can see that the internal sum() function has 3 arguments being passed in so SAS will treat this as the SAS sum() function (as the sql-aggregate statement only allows for a single argument). The result of the SAS function is then passed in as the single parameter to the sql-aggregate sum function:
proc sql noprint;
create table test as
select sex,
sum(sum(height,weight,0)) as sum_height_and_weight
from sashelp.class
group by 1
;
quit;
Result:
proc print data=test;
run;
sum_height_
Obs Sex and_weight
1 F 1356.3
2 M 1728.6
Also note a trick I've used in the code by passing in 0 to the SAS function - this is an easy way to add an additional parameter without changing the intended result. Depending on your data, you may want to swap out the 0 for a null value (ie. .).
EDIT: To address the issue of unknown column names, you can create a macro variable that contains the list of column names you want to sum together:
proc sql noprint;
select name into :varlist separated by ','
from sashelp.vcolumn
where libname='SASHELP'
and memname='CLASS'
and upcase(name) like '%T' /* MATCHES HEIGHT AND WEIGHT */
;
quit;
%put &varlist;
Result:
Height,Weight
Note that you would need to change the above wildcard to match your scenario - ie. matching fields that begin with an underscore, instead of fields that end with the letter T. So your final SQL statement will look something like this:
proc sql noprint;
create table test as
select sex,
sum(sum(&varlist,0)) as sum_of_fields_ending_with_t
from sashelp.class
group by 1
;
quit;
This provides an alternate approach to Joe's answer - though I believe using the view as he suggests is a cleaner way to go.