I have the below macro, PARAMETERS, that assigns values to five variables and creates the table MD_WRK.SUB_ID. My issue is that the code within the macro worked fine until I placed it within the macro. Now the variables are not being populated and the PROC SQL for table creation is not being executed. When I remove "%MACRO" the PROC SQL colors change to indicate that it is correct, but when within the macro they are greyed out. I can't understand why the code within the macro is being invalidated.
%MACRO PARAMETERS;
%GLOBAL FDMVAR1 FDMVAR2 UPBOUND LOBOUND GROUPVAR;
%LET FDMVAR1 ='31Mar2017:0:0:0'dt; /* Start Date */
%LET FDMVAR2 = '30Apr2017:0:0:0'dt; /* End Date */
%LET UPBOUND = .01;
%LET LOBOUND = -.01;
%LET GROUPVAR = 1;
PROC SQL;
CREATE TABLE MD_WRK.SUB_ID
(SUBSYS_ID char(6));
INSERT INTO MD_WRK.SUB_ID
VALUES('CBS')
VALUES('CDS')
VALUES('DDA')
VALUES('IMCOR')
VALUES('ODL')
VALUES('OEC')
VALUES('OEC2')
VALUES('SAV')
VALUES('PWRCDS')
VALUES('SMARTY')
VALUES('MRKTLN')
;
QUIT;
%MEND PARAMETERS;
When you say "PROC SQL colors change" if you are referring to the color of the code as shown in Enterprise Guide or some other client that does automatic syntax coloring for you, I would just ignore the coloring. There are some small glitches in the syntax coloring, but note that these colors are not part of the code, so even when the code coloring suggests something may be wrong, it will execute fine as long as the syntax is correct.
If you turn on options mprint; and invoke your macro, the log should show that the dataset MD_WRK.SUB_ID was created (or give you an error message explaining why it wasn't created).
As #Reeza mentioned, the macro variables your create are probably being created as local macro variables, so they will not exist outside of the macro. If you were to declare them as %global inside the macro, they would exist outside of the macro, because they would be stored in the global symbol table. But usually there are better approaches then relying on global macro variables.
Related
I'm updating a program that uses Hash tables. And the program has a lot of repeated variables that are changed on a regular basis.
I've been updating them to reference a program containing macro code for these variables so we don't have to change them one by one, and it's been working like a charm in all my other projects, but I'm struggling with these hash tables
eg
%let year = x2020;
data acute &year.;
if 0 then set a_&syear.;
if _n_=1 then do;
declare has Tx(dataset:"Ty");
Tx.defineData("city","&year.");
Tx.define();
call missing(city,&year.);
end;
....
run;
I've narrowed it down to the use of &year. in the Tx.defineData line.
It doesn't seem to be picking the macro inside the quotations, and am given this error:
undeclared data symbol &year. for has object
though I usually don't have issues with macros inside quotes.
I've tried changing the let function to %let year = "x2020"; and using dequote() for areas that don't need the quotations, I've also tried using quote(&year.) instead but get
undeclared data symbol" ." for hash object....
Has anyone worked out a way to use a let macro in this scenario?
Your main problem is,
you are trying to use names of datasets and fields that start with a digit. Names in SAS should start with a letter or an underscore and contain only letters, underscores and digits. If not, they should be written differently.
Write "name with blanks"n to create a variable or dataset named name with blanks
Write "1_2_3"n to create a variable or dataset named name with blanks
Now that works, but I advice you not to do so, because the syntax becomes quite complex.
Your code is incomplete and contains a lot of typo's.
Therefore, I have to guess what you actually wanted to do.
It would help if you cut and paste it for us, or better, cut and paste the log, so we know exactly what you did.
The data step
I assume with data acute &year.;, you only wanted to create one dataset, named acute 2020. If so, you should have written data "acute &year."n;, but I actually advice you to rename your dataset and write data acute_&year.;.
You might also have wanted to create datasets acute and 2020 in one datastep. Then you should have written data acute "&year"n.;
The declaration of the hash table
First, it is not declare has, but declare hash and not Tx.define(); but Tx.defineDONE();.
If with Tx.defineData("city","&year."); you wanted to specifies the fields city and 2020 should be used as data, that should work, because here you specify variable names as strings, not as SAS names.
The error is actually in call missing(city,&year.); Here you should use the special syntax call missing(city,"&year."n);
Again, I advice you to rename your variable, for instance to _2020, so you can just write it as _&year.
I am stuck writing a piece of code that features macro variables of the form &&var&i. Let me give a small example:
%macro test;
%let i=1;
%let name1=Tom;
%put &&name&i;
%mend;
%test;
This gives a warning: "Apparent symbolic reference NAME not resolved."
I thought the %put statement should resolve to
1st step: &name1 (&&-->& and &i-->1)
2nd step: Tom
It seems that in the first step already, SAS searches for the macro variable &name, although there are two ampersands in front of it.
This is the SAS log, with symbolgen enabled.
WARNING: Apparent symbolic reference NAME not resolved.
SYMBOLGEN: Macro variable I resolves to 1
&&name1
Am I missing something or are there SAS settings that I should check? Thanks in advance!
Restarting the session seems to have solved the problem for you as Quentin commented.
Other options you can try if the same problem persists is turning on symbolgen option and add dot to resolve the macro variable names %put &&name&i..;
I wish to use select into: to create a global variable in a macro module.
%macro example();
proc sql noprint;
select x into:DDD from aaa;
quit;
%mend;
The problem is I wish to create a global variable DDD, in this example we can only get the local variable DDD.
Thanks.
Wayne.
I don't think you can control the scope within Proc SQL. You an use call symputx in a data step to specify the scope.
However, macro variables created in open code, via Proc SQL, are global by default. Within a macro it will be local.
As noted in the comments you can also explicitly declare a macro variable as global using
%global macro_variable_name;
As #Reeza pointed out you can simply specify that you want the macro-variable global at the beginning of your macro with the %global DDD; statement.
Just one focus point:
sometimes you can recall a macro from inside another macro.
In this case you could have the same macro-variable pre-existing as local in the outer-macro.
Recalling the inner-macro with inside a %global statement you could encounter an error due to the conflict between the global and local definition of the same macro-variable (ERROR: Attempt to %GLOBAL a name (XXX) which exists in a local environment.).
In this case you can:
pre-define the macro-variable as global also in the outer-macro
pre-define the macro-variable as local (or simply give it a (non-)value with a %let, sql into: or call symput) in the outer-macro avoiding the %global statement in the inner-macro
depending on the macro-variable visibility requirement you have.
I have to revamp a SAS project in which there are macro variables such as the following:
%let myDate = intnx('month',today(),-1);
and later...
data temp;
a = &myDate;
run;
I'm inclined to use %sysfunc instead:
%let myDate = %sysfunc(intnx(month,%sysfunc(today()),-1));
But I'm wondering... is this a matter of preference or is there some sound reason to prefer one method over the other?
For extremely large datasets, you might find that with this particular example, calling intnx once for every row to get the same value leads to poor performance vs. defining a macro variable once and re-using it indefinitely.
Rob Penridge has demonstrated that in this instance the overhead is likely to be negligible, but for more computationally intensive code this will obviously not always be the case. Your mileage may vary.
More generally, when you start storing code rather than just constants within macro variables, you have to start thinking quite carefully about certain things:
What sort of macro quoting might or might not be required (particularly in a SAS/CONNECT environment when you need to rsubmit blocks of code using macro variables)
Whether or not your macro variable contains semicolons or other characters that might cause unexpected interactions with other blocks of code
Whether it contains macro references that you don't want to resolve until the code executes
You also need to consider the question of timing. If you use %sysfunc() then the function runs when the macro variable is created. If you just store the function call in the macro variable then the function actually does not execute until the data step is running. And in this case since it is calling the today() function it will run for every observation. If you start your data step just before midnight on the last day of the month you could end up with different values of A on different observations in the same dataset.
These are two different things that you are talking about. The first code with %LET and DATA step will create a macro variable myDate without executing the INTNX function, but will create a Table with column a
But, the second revamped %LET statement will actually create just a macro variable with value of INTNX function executed.
So, it actually depends on what the business requirement is -
To create a Table
OR
create a Macro variable which can be created once and used used over and over again.
It's a matter of personal preference. Personally I like to use the %sysfunc approach as it allows me to debug/print the result without having to process any datasteps.
Really I'd say whatever allows for better readability and maintainability. If you're working with people that hate using macros then consider using the simpler first approach. Horses for courses.
I would like to run the following syntax on lots of variables. Thus, I'd like to loop over a bunch of variables.
The syntax is the following:
compute v3a_mit = v3a.
recode v3a_mit
(-9998=2) (sysmis=9).
exe.
In this case, however, the syntax only concerns the variable "v3a".I have some other variables (v3b, v3c, v3d...) for which I would like to execute this syntax.
So, the loop should look like this.
DO REPEAT X=v3a to v3z
compute concat(X,'_mit') = X.
recode concat(X,'_mit')
(-9998=2) (sysmis=9).
exe.
END REPEAT.
So, within the loop, new variables shall be created which get a new name depending on the variable which is executed in the loop. The "SHIFT VALUES VARIABLE" command would be ideal (with shift=0) but this command cannot be used within a loop. Unfortunately "compute concat(X,'_mit')" does not work either.
CONCAT is a function for manipulating the values of string variables. So you can't use it for defining a variable name.
However you can make use of the !CONCAT function inside of a SPSS macro.
You can use the following macro to recode a set of variables.
DEFINE !recodeVars (vars = !CMDEND)
* for every variable in the 'vars' list do the RECODE INTO command.
!DO !var !IN (!vars)
RECODE !var (-9998=2) (sysmis=9) INTO !CONCAT(!var, '_mit').
!DOEND
!ENDDEFINE.
* macro call.
!recodeVars vars = v3a v3b v3c v3d.
Here, I used the RECODE INTO command, instead of one COMPUTE and a following RECODE command. But of course the principle of how to use the !CONCAT command would be the same for the COMPUTE operation.
However you can't call the macro in way like this !recodeVars vars = v3a TO v3z. In that case the macro would try perform the RECODE for the variables "v3a", "TO" and "v3z". You have to call this macro with the whole list of variables you want to recode.
This might be a lot of typing. As an easy way to avoid the typing, you could produce a SPSS command via the SPSS Menu (for example Analize -> Descreptive Statistics -> Frequencies) Then select the variables you want to recode (select the first variable, hold the SHIFT key and select the last variable) and then press the paste button. The Frequency command with the list of variables will be pasted to your syntax. You can now copy paste the variable list to your macro call.
If you have the Python integration plugin installed you could also use this small python block to retrieve the varlist between two variables.
BEGIN PROGRAM.
import spss,spssaux
variables = 'v3a to v3z' #Specify variables
varlist = spssaux.VariableDict().expand(variables)
spss.SetMacroValue('!varlist', ' '.join(varlist))
END PROGRAM.
This creates a macro named "!varlist" which expands to the list of variables when called.
You can now call the "!recodeVars" macro the following way: !recodeVars vars = !varlist.
If you don't have the python plugin installed (and don't want to use manual typing or copy and pasting) you can get the full variable list with the use of the "!DefList" macro from Raynald's SPSS Tools.
By the way, you can also make use of a macro for the SHIFT VALUES command.