Production Ready Macros for SAS Application Developers
mp_ds2cards.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Create a CARDS file from a SAS dataset.
4  @details Uses dataset attributes to convert all data into datalines.
5  Running the generated file will rebuild the original dataset.
6  usage:
7 
8  %mp_ds2cards(base_ds=sashelp.class
9  , cards_file= "C:\temp\class.sas"
10  , maxobs=5)
11 
12  stuff to add
13  - labelling the dataset
14  - explicity setting a unix LF
15  - constraints / indexes etc
16 
17  @param base_ds= Should be two level - eg work.blah. This is the table that
18  is converted to a cards file.
19  @param tgt_ds= Table that the generated cards file would create. Optional -
20  if omitted, will be same as BASE_DS.
21  @param cards_file= Location in which to write the (.sas) cards file
22  @param maxobs= to limit output to the first <code>maxobs</code> observations
23  @param showlog= whether to show generated cards file in the SAS log (YES/NO)
24  @param outencoding= provide encoding value for file statement (eg utf-8)
25 
26 
27  @version 9.2
28  @author Allan Bowe
29 **/
30 
31 %macro mp_ds2cards(base_ds=, tgt_ds=
32  ,cards_file="%sysfunc(pathname(work))/cardgen.sas"
33  ,maxobs=max
34  ,random_sample=NO
35  ,showlog=YES
36  ,outencoding=
37 )/*/STORE SOURCE*/;
38 %local i setds nvars;
39 
40 %if not %sysfunc(exist(&base_ds)) %then %do;
41  %put WARNING: &base_ds does not exist;
42  %return;
43 %end;
44 
45 %if %index(&base_ds,.)=0 %then %let base_ds=WORK.&base_ds;
46 %if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
47 %if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
48 %if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
49 
50 /* get varcount */
51 %let nvars=0;
52 proc sql noprint;
53 select count(*) into: nvars from dictionary.columns
54  where libname="%scan(%upcase(&base_ds),1)"
55  and memname="%scan(%upcase(&base_ds),2)";
56 %if &nvars=0 %then %do;
57  %put WARNING: Dataset &base_ds has no variables! It will not be converted.;
58  %return;
59 %end;
60 
61 
62 data;run;
63 %let setds=&syslast;
64 proc sql
65 %if %datatyp(&maxobs)=NUMERIC %then %do;
66  outobs=&maxobs;
67 %end;
68  ;
69  create table &setds as select * from &base_ds
70 %if &random_sample=YES %then %do;
71  order by ranuni(42)
72 %end;
73  ;
74 
75 
76 create table datalines1 as
77  select name,type,length,varnum,format,label from dictionary.columns
78  where libname="%upcase(%scan(&base_ds,1))"
79  and memname="%upcase(%scan(&base_ds,2))";
80 
81 /**
82  Due to long decimals (eg in staging.quote_ir) cannot use best. format
83  So - use bestd. format and then use character functions to strip trailing
84  zeros, if NOT an integer!!
85  resolved code = ifc(int(VARIABLE)=VARIABLE
86  ,put(VARIABLE,best32.)
87  ,substrn(put(VARIABLE,bestd32.),1
88  ,findc(put(VARIABLE,bestd32.),'0','TBK')));
89 **/
90 
91 data datalines_2;
92  format dataline $32000.;
93  set datalines1 (where=(upcase(name) not in
94  ('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM')));
95  if type='num' then dataline=
96  cats('ifc(int(',name,')=',name,'
97  ,put(',name,',best32.)
98  ,substrn(put(',name,',bestd32.),1
99  ,findc(put(',name,',bestd32.),"0","TBK")))');
100  else dataline=cats('strip(',name,')') ;
101 run;
102 
103 proc sql noprint;
104 select dataline into: datalines separated by ',' from datalines_2;
105 
106 %local
107  process_dttm_flg
108  valid_from_dttm_flg
109  valid_to_dttm_flg
110 ;
111 %let process_dttm_flg = N;
112 %let valid_from_dttm_flg = N;
113 %let valid_to_dttm_flg = N;
114 data _null_;
115  set datalines1 ;
116 /* build attrib statement */
117  if type='char' then type2='$';
118  if strip(format) ne '' then format2=cats('format=',format);
119  if strip(label) ne '' then label2=cats('label=',quote(trim(label)));
120  str1=catx(' ',(put(name,$33.)||'length=')
121  ,put(cats(type2,length),$7.)||format2,label2);
122 
123 
124 /* Build input statement */
125  if type='char' then type3=':$char.';
126  str2=put(name,$33.)||type3;
127 
128 
129  if(upcase(name) = "PROCESSED_DTTM") then
130  call symputx("process_dttm_flg", "Y", "L");
131  if(upcase(name) = "VALID_FROM_DTTM") then
132  call symputx("valid_from_dttm_flg", "Y", "L");
133  if(upcase(name) = "VALID_TO_DTTM") then
134  call symputx("valid_to_dttm_flg", "Y", "L");
135 
136 
137  call symputx(cats("attrib_stmt_", put(_N_, 8.)), str1, "L");
138  call symputx(cats("input_stmt_", put(_N_, 8.))
139  , ifc(upcase(name) not in
140  ('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM'), str2, ""), "L");
141 run;
142 
143 data _null_;
144  file &cards_file. &outencoding lrecl=32767 termstr=nl;
145  length __attrib $32767;
146  if _n_=1 then do;
147  put '/*******************************************************************';
148  put " Datalines for %upcase(%scan(&base_ds,2)) dataset ";
149  put " Generated by %nrstr(%%)mp_ds2cards()";
150  put " Available on github.com/boemska/macrocore";
151  put '********************************************************************/';
152  put "data &tgt_ds ;";
153  put "attrib ";
154  %do i = 1 %to &nvars;
155  __attrib=symget("attrib_stmt_&i");
156  put __attrib;
157  %end;
158  put ";";
159 
160  %if &process_dttm_flg. eq Y %then %do;
161  put 'retain PROCESSED_DTTM %sysfunc(datetime());';
162  %end;
163  %if &valid_from_dttm_flg. eq Y %then %do;
164  put 'retain VALID_FROM_DTTM &low_date;';
165  %end;
166  %if &valid_to_dttm_flg. eq Y %then %do;
167  put 'retain VALID_TO_DTTM &high_date;';
168  %end;
169  if __nobs=0 then do;
170  put 'call missing(of _all_);/* avoid uninitialised notes */';
171  put 'stop;';
172  put 'run;';
173  end;
174  else do;
175  put "infile cards dsd delimiter=',';";
176  put "input ";
177  %do i = 1 %to &nvars.;
178  %if(%length(&&input_stmt_&i..)) %then
179  put " &&input_stmt_&i..";
180  ;
181  %end;
182  put ";";
183  put "datalines4;";
184  end;
185  end;
186  set &setds end=__lastobs nobs=__nobs;
187 /* remove all formats for write purposes - some have long underlying decimals */
188  format _numeric_ best30.29;
189  length __dataline $32767;
190  __dataline=catq('cqsm',&datalines);
191  put __dataline;
192  if __lastobs then do;
193  put ';;;;';
194  put 'run;';
195  stop;
196  end;
197 run;
198 proc sql;
199  drop table &setds;
200 quit;
201 
202 %if &showlog=YES %then %do;
203  data _null_;
204  infile &cards_file lrecl=32767;
205  input;
206  put _infile_;
207  run;
208 %end;
209 
210 %put NOTE: CARDS FILE SAVED IN:;
211 %put NOTE-;%put NOTE-;
212 %put NOTE- %sysfunc(dequote(&cards_file.));
213 %put NOTE-;%put NOTE-;
214 %mend;