Hello Experts!
I am new to SAS Stored Processes and having some difficulty getting the HTML pages to display after the first prompt. I am attempting to create a SAS stored Process based on dynamic prompts that users will specify and be prompted an additional prompt based on the value of the first one. For simplicity, I am switching to a SASHELP dataset to better show what I am trying to get accomplished.
Data: sashelp.CARS
Stored Process Flow
Prompt1 (dynamic) ---> Prompt2 (dynamic, dependent on prompt1) ---> Report
Prompt1: A list of distinct 'Types' of vehicles that exist in the data (this step works!). The user selects this value and will move to a new page with prompt2.
Prompt2: A list of distinct 'Makes' of vehicles for the specific 'Type' of vehicle specified from the previous screen (this does NOT work!).Getting authorization issue. Note: I am seeing that the value from the first prompt is used and actually creates the table that would be used for the second prompt. Not sure if this is an HTML issue of something else. I have very little knowledge of HTML.
Report: Can't get prompt2 to display so I am not able to validate if this will work properly.
Error when executed in SAS SP Web Application
ERROR: Insufficient authorization to access /opt/sas/sas94m3/SASApp/config/Lev2/SASApp/_webout.dat.
%macro main;
%global reqtype _namevalue cred type make;
filename pwfile "/data/dev/mebell/pw_enc.txt";
data _NULL_;
infile pwfile obs=1 length=l;
input @;
input @1 line $varying1024. l;
call symput('cred',substr(line,1,l));
run;
%macro first;
%stpbegin;
%let _namevalue=%str(_default_entry=firstout.html);
filename firstout "&_stpwork/firstout.html";
/* 1. Get List of Distinct Vehicle Types to Pick */
proc sql;
create table work.vehicle_types as
select distinct type
from sashelp.cars
order by 1;
quit;
run;
/* 2. Select Vehicle Type */
data _NULL_;
set work.vehicle_types end=eof;
file firstout;
if _n_ = 1 then do;
thissrv = "&_URL";
thispgm = "&_PROGRAM";
put '<br>';
put '<h1> List of Vehicle Types </h1>';
put '<FORM ACTION="' thissrv +(-1) '" method=get>';
put '<input type="hidden" name="_program" value="'
thispgm +(-1) '">';
put '<input type="hidden" name=reqtype value="print">';
put '<br>';
put '<b>Select Vehicle Type: </b>';
put '<select name="type">';
end;
if type ne '' then do;
put '<OPTION VALUE="' type '">' type;
end;
if eof then do;
put '</select>';
put '<br><br>';
put '<input type="submit" value="Submit Request">';
put '</form>';
end;
run;
%stpend;
%mend first;
%macro second;
%stpbegin;
/* 3. Get List of Vehicle Make for the Selected Vehicle Type */
%put Vehicle Type previously selected was: &type.;
proc sql;
create table work.vehicle_makes as
select distinct make
from sashelp.cars
where type = "&type."
order by 1;
quit;
run;
/* 4. Select Service Template */
data _NULL_;
set work.vehicle_makes end=eof;
file _webout encoding='utf-8';
if _n_ = 1 then do;
thissrv = "&_URL";
thispgm = "&_PROGRAM";
put '<br>';
put '<h1> List of Vehicle Makes for &type. Vehicles </h1>';
put '<FORM ACTION="&_URL" method=get target="_blank">';
put '<input type="hidden" name="_program" value="'
thispgm +(-1) '">';
put '<br>';
put '<b>Select Vehicle Make: </b>';
put '<select name="make">';
end;
if make ne '' then do;
put '<OPTION VALUE="' make '">' make;
end;
if eof then do;
put '</select>';
put '<br><br>';
put '<input type="submit" value="Submit Request">';
put '</form>';
end;
run;
%stpend;
%mend second;
%macro report;
%stpbegin;
%let _namevalue=%str(_default_entry=main.html);
/* 5. Pull Service Template Definition */
proc sql;
create table work.all_vehicles as
select *
from sashelp.cars
where type = "&type." and make = "&make.";
quit;
run;
/* 6. Display Report for Definition */
title 'List of Vehicles';
proc print data = work.all_vehicles;
run;
%stpend;
%mend report;
%if "&reqtype" = "print2" %then %do;
%report;
%end;
%if "&reqtype" = "print" %then %do;
%second;
%end;
%else %do;
%first;
%end;
%mend main;
%main;
What @FredrikE said about removing the extra STPBEGIN/END calls is correct, but it's a lot more complicated than that.
Try this slightly reworked version of your code to see if it gives you the result that you need.
%global MAKE
TYPE
REQTYPE;
%macro first_prompt;
%* 1. Get list of distinct vehicle types;
proc sql;
create table work.vehicle_types as
select distinct type
from sashelp.cars
order by type;
quit;
%* 2. Create the first prompt;
data _null_;
set work.vehicle_types end=eof;
file _webout encoding='utf-8';
if (_n_ eq 1) then do;
thissrv = "&_URL";
thispgm = "&_PROGRAM";
put '<br>';
put '<h1> List of Vehicle Types </h1>';
put '<form action="' thissrv +(-1) '" method="get">';
put '<input type="hidden" name="_program" value="'
thispgm +(-1) '">';
put '<input type="hidden" name=reqtype value="print">';
put '<br>';
put '<b>Select Vehicle Type: </b>';
put '<select name="type">';
end;
if type ne '' then do;
put '<option value="' type '">' type;
end;
if (eof) then do;
put '</select>';
put '<br><br>';
put '<input type="submit" value="Submit Request">';
put '</form>';
end;
run;
%mend first_prompt;
%macro second_prompt;
%* 3. Get list of vehicle make for the selected vehicle type;
%put Vehicle Type previously selected was: &TYPE;
proc sql;
create table work.vehicle_makes as
select distinct make
from sashelp.cars
where (type eq "&TYPE")
order by make;
quit;
%* 4. Create the second prompt;
data _NULL_;
set work.vehicle_makes end=eof;
file _webout encoding='utf-8';
if _n_ = 1 then do;
thissrv = "&_URL";
thispgm = "&_PROGRAM";
put '<br>';
put "<h1> List of Vehicle Makes for &TYPE Vehicles </h1>";
put '<form action="' thissrv +(-1) '" method="get" target="_blank">';
put '<input type="hidden" name="_program" value="'
thispgm +(-1) '">';
put '<input type="hidden" name="reqtype" value="print2">';
put '<input type="hidden" name="type" value="' "&TYPE" '">';
put '<br>';
put '<b>Select Vehicle Make: </b>';
put '<select name="make">';
end;
if make ne '' then do;
put '<option value="' make '">' make;
end;
if eof then do;
put '</select>';
put '<br><br>';
put '<input type="submit" value="Submit Request">';
put '</form>';
end;
run;
%mend second_prompt;
%macro report;
%stpbegin;
%* 5. Create the report based on prompt selections;
title 'List of Vehicles Based on Prompt Selections';
proc sql;
select *
from sashelp.cars
where (type eq "&TYPE") and (make eq "&MAKE");
quit;
%stpend;
%mend report;
%if (&REQTYPE eq ) %then %do;
%FIRST_PROMPT
%end;
%if (&REQTYPE eq print) %then %do;
%SECOND_PROMPT
%end;
%if (&REQTYPE eq print2) %then %do;
%REPORT
%end;
Vince DelGobbo
SAS R&D
This message:
ERROR: Insufficient authorization to access /opt/sas/sas94m3/SASApp/config/Lev2/SASApp/_webout.dat.
Indicates that the _WEBOUT FILEREF isn't assigned, and SAS is attempting to write to a file in the server startup directory.
Check the stored process definition and make sure that "Stream" is specified for the "Result capabilities" property.
Vince DelGobbo
SAS R&D
Hi Vince - Thank you for the reply and suggestion. I left the code the same and checked the box for the 'stream' option. Now the first prompt output will not display, whereas it did before. See the log below. It stated the &_STPWORK macro variable is not assigned. I removed that variable from the filename and get the same result. Any thoughts what causes the first output to not write to the file?
No Code change with 'Stream' option
WARNING: Apparent symbolic reference _STPWORK not resolved.
NOTE: Table WORK.VEHICLE_TYPES created, with 6 rows and 1 columns.
NOTE: PROCEDURE SQL used (Total process time): real time 0.08 seconds cpu time 0.04 seconds
ERROR: Physical file does not exist, /opt/sas/sas94m3/SASApp/config/Lev2/SASApp/&_stpwork/firstout.html.
Removed Macro Variable &_STPWORK
NOTE: Table WORK.VEHICLE_TYPES created, with 6 rows and 1 columns.
NOTE: PROCEDURE SQL used (Total process time): real time 0.02 seconds cpu time 0.00 seconds
ERROR: Insufficient authorization to access /opt/sas/sas94m3/SASApp/config/Lev2/SASApp/firstout.html.
What @FredrikE said about removing the extra STPBEGIN/END calls is correct, but it's a lot more complicated than that.
Try this slightly reworked version of your code to see if it gives you the result that you need.
%global MAKE
TYPE
REQTYPE;
%macro first_prompt;
%* 1. Get list of distinct vehicle types;
proc sql;
create table work.vehicle_types as
select distinct type
from sashelp.cars
order by type;
quit;
%* 2. Create the first prompt;
data _null_;
set work.vehicle_types end=eof;
file _webout encoding='utf-8';
if (_n_ eq 1) then do;
thissrv = "&_URL";
thispgm = "&_PROGRAM";
put '<br>';
put '<h1> List of Vehicle Types </h1>';
put '<form action="' thissrv +(-1) '" method="get">';
put '<input type="hidden" name="_program" value="'
thispgm +(-1) '">';
put '<input type="hidden" name=reqtype value="print">';
put '<br>';
put '<b>Select Vehicle Type: </b>';
put '<select name="type">';
end;
if type ne '' then do;
put '<option value="' type '">' type;
end;
if (eof) then do;
put '</select>';
put '<br><br>';
put '<input type="submit" value="Submit Request">';
put '</form>';
end;
run;
%mend first_prompt;
%macro second_prompt;
%* 3. Get list of vehicle make for the selected vehicle type;
%put Vehicle Type previously selected was: &TYPE;
proc sql;
create table work.vehicle_makes as
select distinct make
from sashelp.cars
where (type eq "&TYPE")
order by make;
quit;
%* 4. Create the second prompt;
data _NULL_;
set work.vehicle_makes end=eof;
file _webout encoding='utf-8';
if _n_ = 1 then do;
thissrv = "&_URL";
thispgm = "&_PROGRAM";
put '<br>';
put "<h1> List of Vehicle Makes for &TYPE Vehicles </h1>";
put '<form action="' thissrv +(-1) '" method="get" target="_blank">';
put '<input type="hidden" name="_program" value="'
thispgm +(-1) '">';
put '<input type="hidden" name="reqtype" value="print2">';
put '<input type="hidden" name="type" value="' "&TYPE" '">';
put '<br>';
put '<b>Select Vehicle Make: </b>';
put '<select name="make">';
end;
if make ne '' then do;
put '<option value="' make '">' make;
end;
if eof then do;
put '</select>';
put '<br><br>';
put '<input type="submit" value="Submit Request">';
put '</form>';
end;
run;
%mend second_prompt;
%macro report;
%stpbegin;
%* 5. Create the report based on prompt selections;
title 'List of Vehicles Based on Prompt Selections';
proc sql;
select *
from sashelp.cars
where (type eq "&TYPE") and (make eq "&MAKE");
quit;
%stpend;
%mend report;
%if (&REQTYPE eq ) %then %do;
%FIRST_PROMPT
%end;
%if (&REQTYPE eq print) %then %do;
%SECOND_PROMPT
%end;
%if (&REQTYPE eq print2) %then %do;
%REPORT
%end;
Vince DelGobbo
SAS R&D
Hi!
You should not use "file _webout" inside the %stpbegin/end macros.
Instead only use the macros when printing result with for example proc print or proc report, like you have in the %report macro 🙂
Remove the ones you have in %first and %second.
//Fredrik
Fredrik - Thank you for this info. With this along with Vince's suggested code change I have the stored procedure working now. The cascading prompts are working as expected with a dependency on the previous prompt.
Much appreciated, Thank you!
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.