I am new to SAS and I am currently trying to create a macro which will rename variables by replacing all instances of special characters with an underscore. I have come across a specific issue where I am getting an error message because a variable name contains "'". More explicitly,
Suppose we were to have a variable name "dumm'y?", under my macro, this should be renamed to "dumm_y_". The problem is that when I come to performing the renaming what SAS tries to compute is: rename 'dumm'y?'n = dumm_y_". The issue is the apostrophe in the variable name causes SAS to produce an error message.
I have tried to replace the apostrophe with an underscore using TRANSLATE(name, "_", "'"), though this causes errors since SAS will be performing RENAME 'dumm_y?'n = "dumm_y_' as dumm_y? is not a variable name within the database I am trying to rename.
I know that by replacing all instances of ' into a ", vice versa, in name = cats("'", name, "'n") will resolve this issue. Though the problem will still remain if the variable name were dumm"y?.
N.B. I know that by changing SAS variable name policy to V7 will effectively resolve this issue. I am intentionally creating this macro to improve on my SAS skills.
/* Generate dummy data */
option validvarname = any;
data dummy_data;
input "dumm'y?"n;
datalines;
1
2
3
;
run;
data badvarname (keep = name validname);
set sashelp.vcolumn;
where libname = "WORK" and memname = "DUMMY_DATA";
validname = prxchange("s/[^a-zA-Z0-9]/_/", -1, trim(name));
/* Replacing the apostrophe with an underscore still causes an error */
/* name = translate(name, "_", "'"); */
name = cats("'", name, "'n");
run;
proc sql;
select cats("rename", name, "=", validname, ";") into : renamelist
separated by " " from badvarname;
quit;
data output_tab;
set dummy_data;
&renamelist.;
run;
Use the NLITERAL() function to convert the NAME in the metadata into the syntax that is valid for use in SAS code. Do not include the RENAME keyword in the generated macro variable.
proc sql noprint;
select catx('=',nliteral(name),nliteral(validname))
into :renamelist separated by ' '
from badvarname;
quit;
...
rename &renamelist ;
....
Use the NLITERAL function.
name = nliteral(name);
Related
I want to make permanent changes is the variable name Fclass and label Date as Departure date.
i have used the modify statement along with rename but I am getting error when I am running the program.
proc datasets library= ia;
modify passngrs;
rename FClass= First Class;
label Date='Departure date';
format Date date9.;
run;
You might want to do any of these:
Rename to a variable name that does not contain a space
rename FCLASS = FirstClass;
Rename to a trickier name by using session options and a name literal
options validvarname=any;
proc ...;
...;
rename FCLASS = 'First Class'N;
Use a label for FCLASS instead of renaming it
label FCLASS = 'First Class';
I have a macro variable I need to use within PROC SQL. The way it resolves appears to have perfect syntax, but I am getting a syntax error and I'm not sure why;
%let test = mytext;
PROC SQL;
CREATE TABLE myTalbe&test AS
SELECT DISTINCT
a.column
FROM
tablename a
WHERE
a.column = %bquote('&test')
;QUIT;
The error I get throws a red line under the resolved text, 'mytext', and says
ERROR 22-322: Syntax error, expecting one of the following: a name, a
quoted string, a numeric constant, a datetime constant,
a missing value, (, *, +, -, ALL, ANY, BTRIM, CALCULATED, CASE, INPUT, PUT, SELECT, SOME, SUBSTRING, TRANSLATE,
USER.
I don't feel like this error applies here. If I hard code in 'mytext' it works fine. Am I missing something right under my nose? Can anyone lend me a hand?
Thanks!!!
The macro quoting is confusing the SAS parser. For this program I would remove the use of %bquote() and just use double quotes instead of single quotes so that the macro variable reference will resolve.
WHERE a.column = "&test"
If your are actually generating pass thru SQL into a system that requires the use of single quotes for string literals then you will need to use %unquote() to remove the macro quoting.
... from connection to ... ( ...
WHERE a.column = %unquote(%bquote('&test'))
... ) ...
The BQUOTE function tries to resolve the value immediately at execution time. Try removing it and using double quotes instead:
%let test = mytext;
PROC SQL;
CREATE TABLE myTalbe&test AS
SELECT DISTINCT
a.column
FROM
tablename a
WHERE
a.column = "&test"
;QUIT;
So using SAS, I have a number of SAS monthend datasets named as follows:
mydata_201501
mydata_201602
mydata_201603
mydata_201604
mydata_201605
...
mydata_201612
Each has account information at particular monthend. I want to stack the datasets all into one dataset using colon rather than writing out the full set statement as follows:
data mynewdata;
set mydata_:;
run;
However there is no datestamp variable within the datasets so when I stack them I will lose the monthend information for each account. I want to know which line refers to which monthend for each account. Is there a way I can automatically create a variable that names the table the row come from. for example the long winded way would be this:
data mynewdata;
set mydata_201501 (in=a) mydata_201502 (in=b) mydata_201503 (in=c)...;
if a then tablename = 'mydata_201501';
if b then tablename = 'mydata_201502';
if c...
run;
but is there a quicker way using colon along these lines?
data mynewdata;
set mydata_:;
tablename = _tablelabel_;
run;
thanks
I always find clicking on comment links annoying, so hopefully here's the answer in your context. Use the INDSNAME= SET statement option to assign the dataset name to a variable:
data mynewdata;
set mydata_: indsname=_tablelabel_;
tablename = _tablelabel_;
run;
N.B. you can call _tablelabel_ whatever you want, and you may wish to change it so it doesn't look like a SAS generated variable name.
INDSNAME= only became a SAS SET statement option in version 9.2
Just to be clear, with my particular code, where the datasets were named mydata_yyyymm and I wanted a monthend variable with datestamp, I was able to produce this using the solution provided by mjsqu as follows (obs and keep statement provided if required):
data mynewdata;
set mydata_: (obs=100 keep=xxx xxx) indsname=_tablelabel_;
format monthend yymmdd10.;
monthend = input(scan(_tablelabel_,-1,'_'),yymmn6.);
run;
Trying to save data set Keepmerge as a permanent SAS data set called Oct15Tot using the code below. If I sub "&OutTabTot" for just Oct15Tot, it works. Trying to save myself from having to chang another bit of code further down (the %let is referenced at the beginning, and is used throughout my program. Thanks!
%let OutTabTot = Oct15Tot;
libname WorkItem "\\WRKGRP\CVOWB\SAS Data Sets";
data WorkItem."&OutTabTot";
set work.Keepmerge;
run;
Here's the error I'm getting:
22
201
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, /, ;,
_DATA_, _LAST_, _NULL_.
ERROR 201-322: The option is not recognized and will be ignored.
If you remove the quotes in your Data statement it should work, like so:
%let OutTabTot = Oct15Tot;
libname WorkItem "\\WRKGRP\CVOWB\SAS Data Sets";
Data WorkItem.&OutTabTot;
Set Work.Keepmerge;
Run;
In general, as cherry notes, you should just skip the quotations.
However, if you have reason to use quotations, you need to use an n afterwards to tell SAS to make this a name literal.
%let OutTabTot = Oct15 Tot;
options validmemname=extend;
libname WorkItem "\\WRKGRP\CVOWB\SAS Data Sets";
Data WorkItem."&OutTabTot"n;
Set Work.Keepmerge;
Run;
I don't recommend using things like dataset names with spaces if you can avoid it, as it's a pain... but it's legal, with options validmemname=extend set.
I am trying to write a program in SAS using the Prompt manager variable within a PROC SQL statement. It isnt working for me and keeps saying that the symbol is not recognized and will be ignored.
Heres a sample of my code...
LIBNAME mylib ORACLE SCHEMA = 'myschema' PATH = prd USER = 'admin' PASSWORD = 'admin12';
PROC SQL;
SELECT DISTINCT TEST_ID, COUNT(*), TERM
FROM mylib.testtable
WHERE RELEASE = 'PRETEST'
AND TEST_ID IN (&TEST) /* 'MATH', 'READING', 'SCIENCE' */
AND TERM = 'SPRING'
GROUP BY TEST_ID, TERM
ORDER BY TEST_ID, TERM;
QUIT;
And here is the problem in the log:
40 WHERE RELEASE = 'PRETEST'
41 AND TEST_ID IN (&TEST) /* 'MATH', 'READING', 'SCIENCE' */
NOTE: Line generated by the macro variable "TEST".
41 'MATH', 'READING', 'SCIENCE'
_
22
_
200
ERROR 22-322: Syntax error, expecting one of the following: a quoted string, a numeric constant, a datetime constant,
a missing value, (, -, SELECT.
ERROR 200-322: The symbol is not recognized and will be ignored.
My prompt variable is &TEST and should hold the list of tests to take but it dosent.
The issue here is one related to macro quoting. It's apparent that the token is enclosed in macro quotes (similar to %nrstr basically) for some reason, which cause it to work slightly differently than a normal %let. %unquote fixes it. I suspect there is also a better way to define the prompt to cause this not to occur, but I'm not completely sure - maybe one of the more experienced EG
folks can answer.
(Define a TEXT - SINGLE VALUE prompt called type and attach it to a program containing this:)
proc sql;
select name, age
from sashelp.class;
where name in (%unquote(&type.))
;
quit;
OK, I found a solution to my dilemma. As Joe stated, its a macro quoting issue, but it was also an array issue too. Both are solved by wrapping the variable in double quotes and some rudimentary replication.
Long story short,
the way SAS handles arrays and multiple values caused the first value to show only the first value so I had to assign multiple nullable values.
Working product below:
SELECT DISTINCT
TEST_ID, COUNT(*), TERM
FROM mylib.&TABLE
WHERE RELEASE IN ("&RELEASE", "&RELEASE1", "&RELEASE2", "&RELEASE3")
AND TEST_ID IN ("&TEST", "&TEST1", "&TEST2", "&TEST3", "&TEST4", "&TEST5")
AND TERM IN ("&TERM", "&TERM1", "&TERM2", "&TERM3", "&TERM4")
GROUP BY TEST_ID, TERM
ORDER BY TEST_ID, TERM;
Adding the &Release, &Release1, ect. allows multiple values to be captured should there be an array of values, otherwise it would accept the first value default the
extra values to null and throw a warning.
This was sufficient enough to be able to provide a list of options to the user and allow them to run using one or more parameter in each field