BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Crubal
Quartz | Level 8

Hi,

 

I have a question within my sas macro function. 

 

Some exceptional cases that Table A may not have any columns. And I would like the process going if it has columns, and stops when it does not have. How could I make it happen?

 

Thank you! 

1 ACCEPTED SOLUTION

Accepted Solutions
PaigeMiller
Diamond | Level 26

inside your macro

 

run some code

 

check to see if the column exists using the code provided by RW9, where the additional processing is what he calls CodeToExecute (which will execute only if the column exists)

 

end of macro

 

execute macro

 

 

ALTERNATIVE

Create a macro

 

run some code

 

check to see if the column exists

proc sql noprint;
    select count(distinct name) into :does_column_exist
   from sashelp.vcolumn where name='desired_column_name'
        and lib='your_library' and memname='your_data_set_name';
quit;
%if &does_column_exist>0 %then %do;

Note, capitalization is important in name='desired_column_name', so this can be avoided by

lowcase(name)='desired_column_name' in all lower case

 

rest of code

 

 

%end;

 

%mend;

 

Execute macro

--
Paige Miller

View solution in original post

26 REPLIES 26
RW9
Diamond | Level 26 RW9
Diamond | Level 26

Check the metadata:

data _null_;
  set sashelp.vcolumn (where=(libname="<your lib>" and memname="<your dataset>"));
  if _n_=1 then call execute('%macro CodeToExecute;');
run;

This will only call the CodeToExecute macro if in <your lib>.<your dataset> (all in capitals in this case) contains 1 or more columns.

Crubal
Quartz | Level 8

Thanks for your reply @RW9 

 

Sorry I didnot explain clearly. In my case, this part is within a macro process. For example, my pseudo code looks like:

 

%Macro ....

 

Obtain Table A from database.

 

Check if Table A has columns or not. 

 

Run later process. 

 

%Mend; 

 

How can I realize ( Check if Table A has columns or not) 

 

Thank you! 

PaigeMiller
Diamond | Level 26

inside your macro

 

run some code

 

check to see if the column exists using the code provided by RW9, where the additional processing is what he calls CodeToExecute (which will execute only if the column exists)

 

end of macro

 

execute macro

 

 

ALTERNATIVE

Create a macro

 

run some code

 

check to see if the column exists

proc sql noprint;
    select count(distinct name) into :does_column_exist
   from sashelp.vcolumn where name='desired_column_name'
        and lib='your_library' and memname='your_data_set_name';
quit;
%if &does_column_exist>0 %then %do;

Note, capitalization is important in name='desired_column_name', so this can be avoided by

lowcase(name)='desired_column_name' in all lower case

 

rest of code

 

 

%end;

 

%mend;

 

Execute macro

--
Paige Miller
Crubal
Quartz | Level 8

Thanks @PaigeMiller  It works for me. 

Crubal
Quartz | Level 8

Hi @PaigeMiller I tried as you suggested: 

 

data _null_;
set sashelp.vcolumn (where=(libname = "<WORK>" and memname = "<Table A>"));
%if _n_ = 1 %then %do;


Proc Sql;
Create Table Table B as select distinct
...

...

from ...
Quit;


%end;

Run;

 

 

However, even though Table A has columns, the log file shows _n_ = 1 is false, and cannot do later sql procedure. 

 

How could I modify it? 

 

Thanks!

PaigeMiller
Diamond | Level 26

Show us your actual code (at least the relevant parts)

--
Paige Miller
Crubal
Quartz | Level 8

Okay thanks. 

 

The following obtains Table A_&LOC_ID.  Where &LOC_ID differs by location. 

 

proc genmod data=A0_&LOC_ID;
class Mon Dow;
by Operating_Div_cd chain_cd loc_id prod_class_id;
model Cancel_Noshow = Mon Dow/ dist=poisson
link=log scale=pearson;
output out=A_&LOC_ID p=Noshow_Cancel_Pred_Pos;
where Stay_Date between &START1 and &END1;
run;
ods exclude all;

 

 

The following checks if table A_&LOC_ID has any columns or not. And continue the process to get B_&LOC_ID. 

/* Check if table A_&LOC_ID has any columns or not */

data _null_;
set sashelp.vcolumn (where=(libname = "<WORK>" and memname = "<HIST_5_6_Pos_&LOC_ID>"));
%if _n_ = 1 %then %do;

/* Continue the process when _n_ = 1 */
Proc Sql;
Create Table B_&LOC_ID as select distinct 
Operating_Div_Cd,
Chain_Cd,
Loc_Id,
Prod_Class_Id,
Mon,
Dow,
Case 
When Weekday(Stay_Date) in (1,2,3,4,5) then 'WD'
Else 'WE'
End as DY_TYP,
avg(Noshow_Cancel_Pred_Pos) as Noshow_Fcst
from A_&LOC_ID
group by 1, 2, 3, 4, 5, 6, 7;
Quit;

%end;

Run;

 

 

RW9
Diamond | Level 26 RW9
Diamond | Level 26

You have fallen into two common misconceptions:

1) What macro language is compared to Base SAS.  Macro language, all those % and &'s, is a pre-processor, i.e. a limited scope text generation tool.  If scans the text you present and using some simple logic expands the text out.  This full text is then passed on to the Base SAS compiler which compiles and executes the script.  You are using _n_ which is an automatic datastep variable in macro processing, which is not going to work.  You would need to call execute, or put a value into a macro variable.

 

2) Having the thinking that you need to split data out and then loop over that data.  You can simplify your code so much, and save yourself a lot of headaches is dropping this whole thinking.  As an example, you are looping over the data blocking out certain items and then running some models.  Another way of looking at it would be to add into the data another variable, a categorising variable, which holds this blocking information, then include that in the by group processing.  That way you don't loop, but leave the procedure to handle all that.  If you can provide some simple test data, I can show you, but for example, I could take sashelp.class and do a loop over that data based on gender, i.e. it would be %do i=1 % to 2;..., alternatively I could just add gender into the by group.

PaigeMiller
Diamond | Level 26

@Crubal wrote:

Okay thanks. 

 

The following obtains Table A_&LOC_ID.  Where &LOC_ID differs by location. 

 

proc genmod data=A0_&LOC_ID;
class Mon Dow;
by Operating_Div_cd chain_cd loc_id prod_class_id;
model Cancel_Noshow = Mon Dow/ dist=poisson
link=log scale=pearson;
output out=A_&LOC_ID p=Noshow_Cancel_Pred_Pos;
where Stay_Date between &START1 and &END1;
run;
ods exclude all;

 

 

The following checks if table A_&LOC_ID has any columns or not. And continue the process to get B_&LOC_ID. 

/* Check if table A_&LOC_ID has any columns or not */

data _null_;
set sashelp.vcolumn (where=(libname = "<WORK>" and memname = "<HIST_5_6_Pos_&LOC_ID>"));
%if _n_ = 1 %then %do;

/* Continue the process when _n_ = 1 */
Proc Sql;
Create Table B_&LOC_ID as select distinct 
Operating_Div_Cd,
Chain_Cd,
Loc_Id,
Prod_Class_Id,
Mon,
Dow,
Case 
When Weekday(Stay_Date) in (1,2,3,4,5) then 'WD'
Else 'WE'
End as DY_TYP,
avg(Noshow_Cancel_Pred_Pos) as Noshow_Fcst
from A_&LOC_ID
group by 1, 2, 3, 4, 5, 6, 7;
Quit;

%end;

Run;

 

 


Here's the code I suggested:

 

proc sql noprint;
    select count(distinct name) into :does_column_exist
   from sashelp.vcolumn where lowcase(name)='desired_column_name'
        and lowcase(lib)='your_library' and lowcase(memname)='your_data_set_name';
quit;
%if &does_column_exist>0 %then %do;

You haven't really done anything like this. Please note that where I have typed desired_column_name you should type the actual name of the variable in lower case, etc. etc. etc.

 

Also remember there needs to be an %end; statement somewhere below

--
Paige Miller
Crubal
Quartz | Level 8

Hi @PaigeMiller

 

Thanks for your reply, and I tried yours in the very beginning, but result is same. Could you please let me know anything wrong with the following code? Thanks!

		proc genmod data=A0_&LOC_ID;
			class Mon Dow;
			by Operating_Div_cd chain_cd loc_id prod_class_id;
			model Cancel_Noshow = Mon Dow/ dist=poisson
			link=log scale=pearson;
			output out=A_&LOC_ID p=Noshow_Cancel_Pred_Pos;
			where Stay_Date between &START1 and &END1;
		run;
		ods exclude all; 
/* Check if A_&LOC_ID has any columns or not */
Proc Sql noprint;
   Select count(distinct name) into: does_column_exist
      from sashelp.vcolumn where lowcase(name) = 'noshow_cancel_pred_pos' and lowcase(memname) = 'a_&loc_id';
Quit;
       /* Conduct the following part according to the condition */
%if &does_column_exist > 0 %then %do; /* Gather output data by Month and day of week level for Noshow + Cancellation */ Proc Sql; Create Table B_&LOC_ID as select distinct Operating_Div_Cd, Chain_Cd, Loc_Id, Prod_Class_Id, Mon, Dow, Case When Weekday(Stay_Date) in (1,2,3,4,5) then 'WD' Else 'WE' End as DY_TYP, avg(Noshow_Cancel_Pred_Pos) as Noshow_Fcst from A_&LOC_ID group by 1, 2, 3, 4, 5, 6, 7; Quit; %end;
PaigeMiller
Diamond | Level 26

@Crubal wrote:

Hi @PaigeMiller

 

Thanks for your reply, and I tried yours in the very beginning, but result is same. Could you please let me know anything wrong with the following code? Thanks!

/* Check if A_&LOC_ID has any columns or not */
Proc Sql noprint;
   Select count(distinct name) into: does_column_exist
      from sashelp.vcolumn where lowcase(name) = 'noshow_cancel_pred_pos' and lowcase(memname) = 'a_&loc_id';
Quit;

If you have a macro variable, you can't put it inside single quotes. You must put it inside double quotes.

--
Paige Miller
Crubal
Quartz | Level 8

Hi @PaigeMiller

 

Is it like the following? I tried but returns same result. 

 

		Proc Sql noprint;
			Select count(distinct name) into: does_column_exist
			from sashelp.vcolumn where lowcase(name) = "noshow_cancel_pred_pos" and lowcase(memname) = "hist_5_6_pos_&loc_id";
		Quit;

		%if &does_column_exist > 0 %then %do;

Thank you! 

PaigeMiller
Diamond | Level 26

So let's stop right here and discuss some basic necessities of forum usage.

 

You can't say things like "it didn't work", or in your case "but returns same result". We have no idea what you mean. We don't know why it doesn't work or returns the same result.

 

You have to tell us what didn't work, and the best way to do that is to show us the relevant parts of your SASLOG. You also have to show us your SAS code (which you did), and in some cases you have to show us your data.

 

 

 

--
Paige Miller
Crubal
Quartz | Level 8

Thanks @PaigeMiller

 

Code is like following: 

 

 

%macro noshow_fcst;
OPTIONS NOTES SOURCE SOURCE2 MPRINT MLOGIC MERROR SYMBOLGEN;

data _null_;
 set PO_LOC end=last;
 if last then call symput('nfiles',_n_);
run;

%do i=1 %to &nfiles;
	%put i =;

	data _null_;
		set PO_LOC;

		if &i=_n_ then
			call symput('LOC_ID',LOC_ID);
	run;

	%Let Lead_Time = 0;
	
                /* HIST5_&LOC_ID IS obtained from Teradata database */

		/* Transfer pure Noshow and Cancellation from 'arrival date' & 'length of stay' level to 'stay date' */
		DATA HIST5_1_&LOC_ID;
			SET HIST5_&LOC_ID;
				Cancellation_Lead = Arrival_Date - Cancel_Date; 
					if Cancellation_Lead > 30 then Cancellation_Lead = 30;
					If Cancellation_Lead < 0 then Cancellation_Lead = 0;
				FORMAT Stay_Date yymmdd10.;
			do k=1 to length_of_stay;
				Stay_Date = Arrival_Date - 1 + k;
			output;
			end;
			drop k length_of_stay;
		run;

				/* Filter the data as we count 'No-show' for 1 night and Cancellation for Lead Time N */
		Data HIST5_2_&LOC_ID;
			Set HIST5_1_&LOC_ID;
			If Cancellations > 0 and Cancellation_Lead > &Lead_Time then delete;
			If Stay_Date > Arrival_Date and Noshows > 0 then delete;
		Run;
	

		/* Actual Noshow and Cancellation data by Stay Date (Month and Day of Week) level */
		Proc Sql;
			Create Table HIST5_3_&LOC_ID as select distinct
				Operating_Div_Cd,
				Chain_Cd,
				Loc_Id,
				Stay_Date,
				Month(Stay_Date) as Mon,
				Weekday(Stay_Date) as Dow,
				Prod_Class_Id,
				sum(Cancellations+Noshows) as Cancel_Noshow
			from HIST5_2_&LOC_ID
			group by 1, 2, 3, 4, 5, 6, 7
			order by 1, 2, 3, 7, 4;
		Quit;

		/* Forecasting Approach: Poisson Regression, omitting impact of Special Event */
		/* This part could obatin data for Noshow + Cancellation on some Lead Time */
		proc genmod data=HIST5_3_&LOC_ID;
			class Mon Dow;
			by Operating_Div_cd chain_cd loc_id prod_class_id;
			model Cancel_Noshow = Mon Dow/ dist=poisson
			link=log scale=pearson;
			output out=HIST_5_6_Pos_&LOC_ID p=Noshow_Cancel_Pred_Pos;
			where Stay_Date between &START1 and &END1;
		run;
		ods exclude all; 

		/* Check if HIST_5_6_Pos_&LOC_ID has any columns or not */
		Proc Sql noprint;
			Select count(distinct name) into: does_column_exist
			from sashelp.vcolumn where lowcase(name) = "noshow_cancel_pred_pos" and lowcase(memname) = "hist_5_6_pos_&loc_id";
		Quit;

		%if &does_column_exist > 0 %then %do;

		/* Gather output data by Month and day of week level for Noshow + Cancellation */
		Proc Sql;
			Create Table Noshowfcst_&LOC_ID as select distinct 
				Operating_Div_Cd,
				Chain_Cd,
				Loc_Id,
				Prod_Class_Id,
				Mon,
				Dow,
				Case 
					When Weekday(Stay_Date) in (1,2,3,4,5) then 'WD'
					Else 'WE'
				End	as DY_TYP,
				avg(Noshow_Cancel_Pred_Pos) as Noshow_Fcst
			from HIST_5_6_Pos_&LOC_ID
			group by 1, 2, 3, 4, 5, 6, 7;
		Quit;
		
		%end;
			%else %do;
			%end;


				
%end;

%mend;
	/* The first macro ends for No-show Forecasting */

%noshow_fcst;

 

Part of Log Information for HIST_5_6_POS_&LOC_ID: 

 

NOTE: The data set WORK.HIST_5_6_POS_AADAL has 912 observations and 9 variables.

 

SYMBOLGEN:  Macro variable LOC_ID resolves to AADAL

 

MPRINT(NOSHOW_FCST): Select count(distinct name) into: does_column_exist from sashelp.vcolumn where lowcase(name) =
"noshow_cancel_pred_pos" and lowcase(memname) = "hist_5_6_pos_AADAL";
MPRINT(NOSHOW_FCST): Quit;

 

SYMBOLGEN: Macro variable DOES_COLUMN_EXIST resolves to 0
MLOGIC(NOSHOW_FCST): %IF condition &does_column_exist > 0 is FALSE
MLOGIC(NOSHOW_FCST): %DO loop index variable I is now 2; loop will iterate again.

 

 

Part of the data for 'hist_5_6_pos_&loc_id' : 

 

Question.PNG

 

 

Thank you! And for this example, 'Noshowfcst_&LOC_ID' cannot be obtained even if 'hist_5_6_pos_&LOC_ID' exists. 

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

How to connect to databases in SAS Viya

Need to connect to databases in SAS Viya? SAS’ David Ghan shows you two methods – via SAS/ACCESS LIBNAME and SAS Data Connector SASLIBS – in this video.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 26 replies
  • 4355 views
  • 1 like
  • 3 in conversation