How to create a MACRO to create a JSON file from dataset - sas

I have some problems when I'm trying to generate a JSON file from dataset on SAS GUIDE. I generated a TEST.JSON:
{"TP_SMS":"1" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"2" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"3" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"4" "NM_REMETENTESMS":"00000159"},
{"TP_SMS":"5" "NM_REMETENTESMS":"00000159"},
.
.
.
{"TP_SMS":"9" "NM_REMETENTESMS":"00000159"},
The field TP_SMS is filled correct, but the second field is wrong - they are considering just the last position from my table.
Below there is my code white macro:
data teste30;
set MATABLES.EXIT_DATA;
RESP=cat(CD_CLIENTE,"|",ANWER_DATA);
ID=_N_;
call symputx('ID',ID);
call symputx('CD_CLIENTE',CD_CLIENTE);
call symputx('NM_PRIMNOMECLIENTE',NM_PRIMNOMECLIENTE);
call symputx('RESP',RESP);
call symputx('msgtext',msgtext);
run;
%macro MontaJSON(ID);
WRITE OPEN OBJECT;
WRITE VALUES "TP_SMS" "&ID";
WRITE VALUES "NM_REMETENTESMS" "&CD_CLIENTE";
WRITE CLOSE;
%mend MontaJSON(ID);
%macro SMSRecords;
%do i = 1 %to &dim_IDs;
%MontaJSON(&&&ID_&i);
%end;
%mend SMSRecords;
proc sql;
select id, CD_CLIENTE into :ID_1 - :ID_&SysMaxLong from work.teste30;
%let dim_IDs = &sqlObs;
quit;
proc json out="C:\TEMP\TEST.json" pretty nokeys nosastags;
write open array; /* container for all the data */
%SMSRecords;
write close; /* container for all the data */
run;
I expect this macro get all datas on sequence, as TP_SMS code:
{"TP_SMS":"1" "NM_REMETENTESMS":"00014578"},
{"TP_SMS":"2" "NM_REMETENTESMS":"21323445"},
{"TP_SMS":"3" "NM_REMETENTESMS":"23456753"},
{"TP_SMS":"4" "NM_REMETENTESMS":"00457663"},
{"TP_SMS":"5" "NM_REMETENTESMS":"00014795"},
{"TP_SMS":"6" "NM_REMETENTESMS":"00014566"},
{"TP_SMS":"7" "NM_REMETENTESMS":"00014578"},
{"TP_SMS":"8" "NM_REMETENTESMS":"00000122"},
{"TP_SMS":"9" "NM_REMETENTESMS":"00000159"}
Does anyone has some idea to solve it?
Tks

I would avoid generating all of those macro variables that are confusing your code.
Instead for that simple format you can just write the file directly instead of using PROC JSON.
data _null_;
set MATABLES.EXIT_DATA end=eof;
file "C:\TEMP\TEST.json" ;
if _n_=1 then put '[';
if not eof then delim=',';
put '{"TP_SMS":' id :$quote. ' "NM_REMETENTESMS":' CD_CLIENTE :$quote. '}' delim ;
if eof then put ']';
run;
Or if you really find that PROC JSON helps then use a similar data step to write the lines of code and use %INCLUDE to run the generated code.
filename code temp;
data _null_;
set MATABLES.EXIT_DATA ;
file code ;
put 'WRITE OPEN OBJECT;'
/ 'WRITE VALUES "TP_SMS" ' ID :$quote. ';'
/ 'WRITE VALUES "NM_REMETENTESMS" ' CD_CLIENTE :$quote. ';'
/ 'WRITE CLOSE;'
;
run;
proc json out="C:\TEMP\TEST.json" pretty nokeys nosastags;
write open array; /* container for all the data */
%include code;
write close; /* container for all the data */
run;

This line is your problem, it will only hold the last data point.
call symputx('CD_CLIENTE',CD_CLIENTE);
Instead, create a value for each ID, similar to how you created the IDs.
call symputx(catx('_', 'CD_CLIENTE', put(i, 8.-l)), CD_CLIENTE);
Then use it later on as &&&CD_CLIENTE&i

Related

Error with label while Proc export in excel sas

I have some data set. (name - table, field - Field1 label="current.field")
When I do
proc export data=work.table label;
outfile = 'bla bla';
DBMS=Excelcs;
run;
I get an error:
CLI execute error: [Microsoft][ODBC Excel
Driver] 'current.field' is not a valid name. Make sure that it does not include
invalid characters or punctuation and that it is not too long..
I know that the problem is in label - it contains ".". But I need this label fro my need. Do anyone knows how to solve this problem? Than you.
If you need your SAS labels with periods (.) in Excel (row 2) you can try the approach outlined in this blog post.
Simply add the label keyword to the proc export step as follows:
/* send data */
PROC EXPORT DATA=&libds OUTFILE=_webout DBMS=&type REPLACE LABEL;
run;
Then, when setting up the web query, ensure your target is a cell in row 2 (eg A2). This worked fine for me, as follows:
One thing you can do in SAS, is to modify those labels so that they will play nicely with the Excel driver. The following xl_label() macro will scan your table and remove any periods (.) from your labels.
/* sample data */
data work.table;
label x ='test1.invalid' y='test2.invalid';
x=1; y='age';
run;
%macro xl_label(lib=,ds=);
/* get labels */
ods output variables=vars(keep=variable label);
proc contents data=&lib..ds; run;
data _null_; set vars;
/* modify labels to make them valid for excel */
label=tranwrd(label,'.','_');
/* send to macro variable array */
call symputx(cats('label',_n_)
,"label "!!variable!!'="'!!label!!'";'
,'l');
run;
/* update table metadata */
proc datasets nolist library=&lib;
modify &ds;
%local x;
%let x=1;
%do %while (%symexist(label&x));
&&label&x /* label statements */
%let x=%eval(&x+1);
%end;
%mend;
%xl_label(lib=work, ds=table);

sas macro to read multiple multsheet excel in folder

this is my code to read multiple multisheet excel in sas its giving an error which i am attaching in last.i am only reading first sheet named summary of every excel in that particular folder
%macro sks2sas01(input=d:\excels,out=work.tt);
/* read files in directory */
%let dir=%str(%'dir %")&input.%str(\%" /A-D/B/ON%');
filename myfiles pipe %unquote(&dir);
data list1; length fname $256.;
infile myfiles truncover;
input myfiles $100.;
/* put _infile_;*/
fname=quote(upcase(cats("&input",'\',myfiles)));
out="&out";
drop myfiles;
call execute('
PROC IMPORT DBMS=EXCEL2002 OUT= _1
DATAFILE= '||fname||' REPLACE ;
sheet="summary";
RUN;
proc append data=_1 base='||out||' force; run;
proc delete data=_1; run;
');
run;
filename myfiles clear;
%mend sks2sas01;
%sks2sas01(input=c:\sasupload\excels,out=work.tt);
hereby i am attaching error i am getting:
GOPTIONS ACCESSIBLE;
%macro sks2sas01(input=d:\excels,out=work.tt);
/* read files in directory */
%let dir=%str(%'dir %")&input.%str(\%" /A-D/B/ON%');
filename myfiles pipe %unquote(&dir);
data list1; length fname $256.;
infile myfiles truncover;
input myfiles $100.;
/* put _infile_;*/
fname=quote(upcase(cats("&input",'\',myfiles)));
out="&out";
drop myfiles;
call execute('
PROC IMPORT DBMS=EXCEL2002 OUT= _1
DATAFILE= '||fname||' REPLACE ;
sheet="summary";
RUN;
proc append data=_1 base='||out||' force; run;
proc delete data=_1; run;
');
run;
filename myfiles clear;
%mend sks2sas01;
%sks2sas01(input=c:\sasupload\excels,out=work.tt);
ERROR: Insufficient authorization to access PIPE.
ERROR: Error in the FILENAME statement.
I had this exact same problem the other day. I had no authorization for the pipe command, and dir using sftp wasn't working for me either. This alternative solution worked great for me. In a nutshell, you're going to use some oldschool SAS directory commands to read every file within that directory, and save only the ones that end in .xlsx.
You can consider the name of your Excel files .-delimited, scan the filename of each one backwards, and look at just the first word to obtain only those Excel files. For example:
File name.xlsx
Backwards:
Delimiter
v
xlsx.name File
^^^^
First word
Step 1: Read all XLSX files in the directory, and create a dataset of them
filename dir 'C:\MyDirectory';
data XLSX_Files;
DirID = dopen("dir");
do i = 1 to dnum(DirID);
file_name = dread(DirID, i);
if(upcase(scan(file_name, 1, '.', 'b') ) = 'XLSX') then output;
end;
rc=dclose(DirID);
drop i rc DirID;
run;
Step 2: Read all of those names into a pipe-delimited macro variable
proc sql noprint;
select file_name, count(file_name)
into :XLSX_Files separated by '|',
:tot_files
from XLSX_Files;
quit;
Step 3: Import them all in a macro loop
%macro import_all;
%do i = 1 %to &tot_files;
proc import file="C:\MyDirectory\%scan(&XLSX_Files,&i,|)"
out=XLSX_&i
dbms=xlsx replace;
run;
%end;
%mend;
%import_all;
You can then stack or merge them as you need.
data Master_File;
set XLSX_1-XLSX_&tot_files;
run;
OR
data Master_File;
merge XLSX_1-XLSX_&tot_files;
by key;
run;

SAS Export data to create standard and comma-delimited raw data files

i m new to sas and studying different ways to do subject line task.
Here is two ways i knew at the moment
Method1: file statement in data step
*DATA _NULL_ / FILE / PUT ;
data _null_;
set engappeal;
file 'C:\Users\1502911\Desktop\exportdata.txt' dlm=',';
put id $ name $ semester scoreEng;
run;
Method2: Proc Export
proc export
data = engappeal
outfile = 'C:\Users\1502911\Desktop\exportdata2.txt'
dbms = dlm;
delimiter = ',';
run;
Question:
1, Is there any alternative way to export raw data files
2, Is it possible to export the header also using the data step method 1
You can also make use of ODS
ods listing file="C:\Users\1502911\Desktop\exportdata3.txt";
proc print data=engappeal noobs;
run;
ods listing close;
You need to use the DSD option on the FILE statement to make sure that delimiters are properly quoted and missing values are not represented by spaces. Make sure you set your record length long enough, including delimiters and inserted quotes. Don't worry about setting it too long as the lines are variable length.
You can use CALL VNEXT to find and output the names. The LINK statement is so the loop is later in the data step to prevent __NAME__ from being included in the (_ALL_) variable list.
data _null_;
set sashelp.class ;
file 'class.csv' dsd dlm=',' lrecl=1000000 ;
if _n_ eq 1 then link names;
put (_all_) (:);
return;
names:
length __name__ $32;
do while(1);
call vnext(__name__);
if upcase(__name__) eq '__NAME__' then leave;
put __name__ #;
end;
put;
return;
run;

Need to repeate data reading from multiple files using sas and run freqs on separate dataset created from separate files

I am new to SAS and facing few difficulties while creating following program.
My requirement is to pass the filename generated dynamically and read it so that don't have to write code five times to read data from 5 different files and then run freqs on the datasets.
I have provided the code below and have to write this code for more than 50 files:
Code
filename inp1 '/chshttp/prod/clients/coms/raw/coms_coms_relg_f1102_t1102_c10216_vEL5535.raw';
filename inp2 '/chshttp/prod/clients/coms/raw/coms_coms_relg_f1103_t1103_c10317_vEL8312.raw';
filename inp3 '/chshttp/prod/clients/coms/raw/coms_coms_relg_f1104_t1104_c10420_vEL11614.raw';
filename inp4 '/chshttp/prod/clients/coms/raw/coms_coms_relg_f1105_t1105_c10510_vEL13913.raw';
filename inp5 '/chshttp/prod/clients/coms/raw/coms_coms_relg_f1106_t1106_c10628_vEL17663.raw';
data test;
Do i = 1 to 5;
infile_name = 'inp' || i;
infile infile_name recfm = v lrecl=1800 end=eof truncover;
INPUT
#1 E_CUSTDEF1_CLIENT_ID $CHAR5.
#1235 E_MED_PLAN_CODE $CHAR20.
#1090 MED_INS_ELIG_COVERAGE_IND $CHAR20.
#1064 MED_COVERAGE_BEGIN_DATE $CHAR8.
#1072 MED_COVERAGE_TERM_DATE $CHAR8.
;
if E_CUSTDEF1_CLIENT_ID ='00002' then
output test;
end;
run;
proc freq data = test;
tables E_CUSTDEF1_CLIENT_ID*E_MED_PLAN_CODE / list missing;
run;
Please help!!
Here's an example you can adapt. There are different ways to do this, but this is one- depending no how you want the frequencies.
Step 1: Create a dataset, 'my_filenames', that stores the filename you want to read in, one per line, in a variable FILE_NAME.
Step 2: Read in the files.
data my_data;
set my_filenames;
infile a filevar=file_name <the rest of your options>;
<your input statement>;
run;
proc freq data=mydata;
by file_name;
<your table statements>;
run;
This is simple, data driven code that doesn't require macros or storing large amounts of data in things that shouldn't have data in them (macro variables, filenames, etc.)
To directly answer your question, here is a SAS macro to read each file and run PROC FREQ:
%macro freqme(dsn);
data test;
infile "&dsn" recfm = v lrecl=1800 end=eof truncover;
INPUT #1 E_CUSTDEF1_CLIENT_ID $CHAR5.
#1235 E_MED_PLAN_CODE $CHAR20.
#1090 MED_INS_ELIG_COVERAGE_IND $CHAR20.
#1064 MED_COVERAGE_BEGIN_DATE $CHAR8.
#1072 MED_COVERAGE_TERM_DATE $CHAR8.
;
if E_CUSTDEF1_CLIENT_ID = '00002';
run;
proc freq data=test;
tables E_CUSTDEF1_CLIENT_ID*E_MED_PLAN_CODE / list missing;
run;
proc delete data=test;
run;
%mend;
%freqme(/chshttp/prod/clients/coms/raw/coms_coms_relg_f1102_t1102_c10216_vEL5535.raw);
%freqme(/chshttp/prod/clients/coms/raw/coms_coms_relg_f1103_t1103_c10317_vEL8312.raw);
%freqme(/chshttp/prod/clients/coms/raw/coms_coms_relg_f1104_t1104_c10420_vEL11614.raw);
%freqme(/chshttp/prod/clients/coms/raw/coms_coms_relg_f1105_t1105_c10510_vEL13913.raw);
%freqme(/chshttp/prod/clients/coms/raw/coms_coms_relg_f1106_t1106_c10628_vEL17663.raw);
Note that I added a PROC DELETE step to delete the SAS data set after creating the report. I did that more for illustration, since you don't say you need the file as a SAS data set for subsequent processing.
You can use this as a template for other macro programming.

How can I perform a macro for each observation in sas data set?

Here is the macro code.....
libname myfmt "&FBRMrootPath./Formats";
%macro CreateFormat(DSN,Label,Start,fmtname,type);
options mprint mlogic symbolgen;
%If &type='n' %then %do;
proc sort data=&DSN out=Out; by &Label;
Run;
Data ctrl;
set Out(rename=(&Label=label &Start=start )) end=last;
retain fmtname &fmtname type &type;
output;
If last then do;
hlo='O';
label='*ERROR';
output;
End;
Run;
%End;
%Else %do;
proc sort data=&DSN out=Out; by &Start;
Run;
Data ctrl;
set Out(rename=(&Start=label &Label=start )) end=last;
retain fmtname &fmtname type &type;
output;
If last then do;
hlo='O';
label='*ERROR';
output;
End;
Run;
%End;
proc format library=myfmt cntlin=ctrl;
Run;
%Mend CreateFormat;
Here is the code for control data set through which above macro should run for each observation of the data set and the values of the observations are inputs for varibales in the macro....
Data OPER.format_control;
Input DSN :$12. Label :$15. Start :$15. fmtName :$8. type :$1. fmt_Startdt :mmddyy. fmt_Enddt :mmddyy.;
format fmt_Startdt fmt_Enddt date9.;
Datalines;
ssin.prd prd_nm prd_id mealnm n . 12/31/9999
ssin.prd prd_id prd_nm mealid c . 12/31/9999
ssin.fac fac_nm onesrc_fac_id fac1SRnm n . 12/31/9999
ssin.fac fac_nm D3_fac_id facD3nm n . 12/31/9999
ssin.fac onesrc_fac_id D3_fac_id facD31SR n . 12/31/9999
oper.wrkgrp wrkgrp_nm wrkgrp_id grpnm n . 12/31/9999
;
Something like this.
proc sql;
select catx(',',cats('%CreateFormat(',DSN),Label,Start,fmtname,cats(type,')');
into :formcreatelist separated by ' '
from oper.format_control;
quit;
You may need to PUT some of your variables to get the format you want into the macro variable. I use the slightly cludgy cats/catx combo here, you could cats once with ',' added in a bunch of times also.
You do have a limit here - around 20,000 characters total in a macro variable. If it's over that, you either have to use CALL EXECUTE (which has some quirky features) or you can put the macro call into a text file and %INCLUDE it.
There is a better way to do this rather than select ... into a macro variable. Use a temp file like this:
filename dyncode temp;
data _null_;
file dyncode;
set OPER.format_control;
put '%createformat ....';
run;
%include dyncode;
filename dyncode clear;
This technique is not limited by the 32k length limitation on macro variables.
Note that you should definitely use single quotes around the %createformat to prevent SAS from invoking the macro just prior to data step compilation. You want the macro to run when the %include runs.
The above approach is analogous to call execute, but call execute is evil because it does not execute the macro and embedded data/proc code within the macro in the expected order. Avoid call execute.
Finally, if you are running interactive SAS and using the technique there is a neat trick you can use to debug. Comment out the last two lines of code -- the include and the filename clear. After you run the remaining code, enter the SAS command "fslist dyncode" in the command window. This will pop up a notepad view on the dynamic code you just generated. You can review it and make sure you got what you intended.
Here's a call execute solution, just for completeness:
data _null_;
set OPER.format_control;
call execute('%CreateFormat(' || DSN || ',' || Label || ',' || Start || ',' || fmtname || ',' || type || ');');
run;