Macros ( remove spaces within variable) - sas

I want to create a table like-
%let x =ab bc;
Proc sql;
create table rx_&x as
select * from test;
quit;
Not able to create, have used compress and %str as well.

Use %sysfunc() in order to use SAS functions in macro code.
%let x=ab bc;
proc sql;
create table rx_%sysfunc(compress(&x.)) as
select *
from test
;
quit;

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.

SAS insert value with proc sql

So I have a rather interesting problem. I am trying to insert a current date in specific formats and styles, but for some reason it seems to fail. I know its not a formatting issue... But idk how to fix it. a data step solution is welcomed as well... Here's what works.
proc sql;
create table work.test
(test_Id char(50), test_Name char(50), cur_Mo char(1), cur_Qtr char(1), entered_Date char(8));
insert into work.test
values('201703','2017 Mar','0','0','24APR17')
values('201704','2017 Apr','0','0','24APR17')
values('201706','2017 Jun','1','0','23JUN17');
quit;
Here's what doesn't:
proc sql;
insert into work.test
values(catx('',put(year(today()),4.),case when month(today())< 10 then catx('','0',put(month(today()),2.)) else put(month(today()),2.)end) ,catx(' ',[put(year(today()),4.),put(today(),monname3.))],'1','0',put(today(),date7.));
quit;
You can use the %SYSFUNC() macro function to call most other SAS function in macro code. So to generate today's date in DATE7 format you could use:
insert into work.test (date)
values("%sysfunc(date(),date7)")
;
The way I'd probably do it is to use a data step to make a dataset that you would insert, and then insert that dataset.
You can use insert into (...) select (...) from (...) syntax in SAS, and the data step is much more flexible as to allowing you to define columns.
For example:
proc sql;
create table class like sashelp.class;
quit;
proc sql;
insert into class
select * from sashelp.class;
quit;
Or you can specify only certain variables:
proc sql;
insert into class (name, age)
select name, age from sashelp.class;
quit;
data to_insert;
name= 'Wilma';
sex = 'F';
age = 29;
height = 61.2;
weight = 95.3;
run;
proc sql;
insert into class
select * from to_insert;
quit;
Just make sure you either explicitly list the variables to insert/select, or you have the order exactly right (it matches up by position if you use * like I do above).

How to count distinct of the concatenation/cross of two variables in SAS Proc Sql?

I know in teradata or other sql platforms you can find the count distinct of a combination of variables by doing:
select count(distinct x1||x2)
from db.table
And this will give all the unique combinations of x1,x2 pairs.
This syntax, however, does not work in proc sql.
Is there anyway to perform such a count in proc sql?
Thanks.
That syntax works perfectly fine in PROC SQL.
proc sql;
select count(distinct name||sex)
from sashelp.class;
quit;
If the fields are numeric, you must put them to character (using put) or use cat or one of its siblings, which happily take either numeric or character.
proc sql;
select count(distinct cats(age,sex))
from sashelp.class;
quit;
This maybe redundant, but when you mentioned "combination", it instantly triggered 'permutation' in my mind. So here is one solution to differentiate these two:
DATA TEST;
INPUT (X1 X2) (:$8.);
CARDS;
A B
B A
C D
C D
;
PROC SQL;
SELECT COUNT(*) AS TOTAL, COUNT(DISTINCT CATS(X1,X2)) AS PERMUTATION,
COUNT(DISTINCT CATS(IFC(X1<=X2,X1,X2),IFC(X1>X2,X1,X2))) AS COMBINATION
FROM TEST;
QUIT;

SAS : How to iterate a dataset elements within the proc sql WHERE statement?

I need to create multiple tables using proc sql
proc sql;
/* first city */
create table London as
select * from connection to myDatabase
(select * from mainTable
where city = 'London');
/* second city */
create table Beijing as
select * from connection to myDatabase
(select * from mainTable
where city = 'Beijing');
/* . . the same thing for other cities */
quit;
The names of those cities are in the sas table myCities
How can I embed the data step into proc sql in order to iterate through all cities ?
proc sql noprint;
select quote(city_varname) into :cities separated by ',' from myCities;
quit;
*This step above creates a list as a macro variable to be used with the in() operator below. EDIT: Per Joe's comment, added quote() function so that each city will go into the macro-var list within quotes, for proper referencing by in() operator below.
create table all_cities as
select * from connection to myDatabase
(select * from mainTable
where city in (&cities));
*this step is just the step you provided in your question, slightly modified to use in() with the macro-variable list defined above.
One relatively simple solution to this is to do this entirely in a data step. Assuming you can connect via libname (which if you can connect via connect to you probably can), let's say the libname is mydb. Using a similar construction to Max Power's for the first portion:
proc sql noprint;
select city_varname
into :citylist separated by ' '
from myCities;
select cats('%when(var=',city_varname,')')
into :whenlist separated by ' '
from myCities;
quit;
%macro when(var=);
when "&var." output &var.;
%mend when;
data &citylist.;
set mydb.mainTable;
select(city);
&whenlist.;
otherwise;
end;
run;
If you're using most of the data in mainTable, this probably wouldn't be much slower than doing it database-side, as you're moving all of the data anyway - and likely it would be faster since you only hit the database once.
Even better would be to pull this to one table (like Max shows), but this is a reasonable method if you do need to create multiple tables.
You need to put your proc sql code into a SAS Macro.
Create a macro-variable for City (in my example I called the macro-variable "City").
Execute the macro from a datastep program. Since the Datastep program processes one for each observation, there is no need to create complex logic to iterate.
data mycities;
infile datalines dsd;
input macrocity $ 32.;
datalines;
London
Beijing
Buenos_Aires
;
run;
%macro createtablecity(city=);
proc sql;
/* all cities */
create table &city. as
select * from connection to myDatabase
(select * from mainTable
where city = "&city.");
quit;
%mend;
data _null_;
set mycities;
city = macrocity;
call execute('%createtablecity('||city||')');
run;
Similar to the other solutions here really, maybe a bit simpler... Pull out a distinct list of cities, place into macros, run SQL query within a do loop.
Proc sql noprint;
Select distinct city, count(city) as c
Into :n1-:n999, :c
From connection to mydb
(Select *
From mainTable)
;
Quit;
%macro createTables;
%do a=1 %to &c;
Proc sql;
Create table &&n&a as
Select *
From connection to myDb
(Select *
From mainTable
Where city="&&n&a")
;
Quit;
%end;
%mend createTables;
%createTables;

why does the MAX function strip the SAS variable format in proc sql?

Consider the following:
data;
format x datetime19.;
x=datetime();
flag='FIRST';
do x=datetime() to x+10 by 1;
output;
flag='';
end;
proc sql noprint;
select x into : test1 from &syslast where flag='FIRST';
select max(x) into: test2 from &syslast;
%put now we see that &test1 is in a different format to &test2;
data _null_; set;
put x=; /* formatted */
call symput('test3',x);
call symput('test4',max(x,sum(x+1000)));
stop;
run;
%put The data step is more consistent - &test3 = &test4;
Seems inconsistent to me. Why does proc sql retain the format in this case? Is this behaviour documented?
There's no way for SAS to know how the result of a function should be formatted. In this case your max() function is simply returning a datetime, but what if there were nested functions or arithmetic inside it. Because of this SAS just treats it as a brand new variable which by default has no format set. If you would like to apply the same format to it you can change your code to this:
select max(x) format=datetime19. into: test2 from &syslast;
In your example, the MAX function creates a new column and when creating new columns, you need to specify all column attributes. So, just add a FORMAT clause to your select statement:
proc sql noprint;
select x into : test1 from &syslast where flag='FIRST';
select max(x) format=datetime19. into: test2 from &syslast;
quit;
%put now we see that &test1 is in a different format to &test2;