@sasjs/utils 2.49.0 → 2.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fs/internal/helper.js +19 -24
- package/fs/internal/helper.js.map +1 -1
- package/macros/mf_abort.sas +27 -0
- package/macros/mf_dedup.sas +53 -0
- package/macros/mf_deletefile.sas +31 -0
- package/macros/mf_existds.sas +30 -0
- package/macros/mf_existfeature.sas +59 -0
- package/macros/mf_existfileref.sas +33 -0
- package/macros/mf_existfunction.sas +37 -0
- package/macros/mf_existvar.sas +39 -0
- package/macros/mf_existvarlist.sas +56 -0
- package/macros/mf_fmtdttm.sas +42 -0
- package/macros/mf_getapploc.sas +78 -0
- package/macros/mf_getattrc.sas +34 -0
- package/macros/mf_getattrn.sas +34 -0
- package/macros/mf_getengine.sas +54 -0
- package/macros/mf_getfilesize.sas +64 -0
- package/macros/mf_getfmtlist.sas +61 -0
- package/macros/mf_getfmtname.sas +44 -0
- package/macros/mf_getkeyvalue.sas +32 -0
- package/macros/mf_getplatform.sas +75 -0
- package/macros/mf_getquotedstr.sas +57 -0
- package/macros/mf_getschema.sas +43 -0
- package/macros/mf_getuniquefileref.sas +60 -0
- package/macros/mf_getuniquelibref.sas +57 -0
- package/macros/mf_getuniquename.sas +22 -0
- package/macros/mf_getuser.sas +44 -0
- package/macros/mf_getvalue.sas +36 -0
- package/macros/mf_getvarcount.sas +49 -0
- package/macros/mf_getvarformat.sas +72 -0
- package/macros/mf_getvarlen.sas +56 -0
- package/macros/mf_getvarlist.sas +74 -0
- package/macros/mf_getvarnum.sas +58 -0
- package/macros/mf_getvartype.sas +52 -0
- package/macros/mf_getxengine.sas +43 -0
- package/macros/mf_increment.sas +29 -0
- package/macros/mf_isblank.sas +27 -0
- package/macros/mf_isdir.sas +34 -0
- package/macros/mf_isint.sas +36 -0
- package/macros/mf_islibds.sas +40 -0
- package/macros/mf_loc.sas +29 -0
- package/macros/mf_mkdir.sas +67 -0
- package/macros/mf_mval.sas +19 -0
- package/macros/mf_nobs.sas +26 -0
- package/macros/mf_trimstr.sas +50 -0
- package/macros/mf_uid.sas +21 -0
- package/macros/mf_verifymacvars.sas +72 -0
- package/macros/mf_wordsinstr1andstr2.sas +53 -0
- package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
- package/macros/mf_writefile.sas +67 -0
- package/macros/mp_abort.sas +301 -0
- package/macros/mp_appendfile.sas +57 -0
- package/macros/mp_applyformats.sas +181 -0
- package/macros/mp_assert.sas +56 -0
- package/macros/mp_assertcols.sas +145 -0
- package/macros/mp_assertcolvals.sas +168 -0
- package/macros/mp_assertdsobs.sas +121 -0
- package/macros/mp_assertscope.sas +147 -0
- package/macros/mp_base64copy.sas +117 -0
- package/macros/mp_binarycopy.sas +79 -0
- package/macros/mp_chop.sas +194 -0
- package/macros/mp_cleancsv.sas +78 -0
- package/macros/mp_cntlout.sas +85 -0
- package/macros/mp_copyfolder.sas +84 -0
- package/macros/mp_coretable.sas +73 -0
- package/macros/mp_createconstraints.sas +70 -0
- package/macros/mp_createwebservice.sas +32 -0
- package/macros/mp_csv2ds.sas +140 -0
- package/macros/mp_deleteconstraints.sas +52 -0
- package/macros/mp_deletefolder.sas +70 -0
- package/macros/mp_dictionary.sas +52 -0
- package/macros/mp_dirlist.sas +263 -0
- package/macros/mp_distinctfmtvalues.sas +50 -0
- package/macros/mp_dropmembers.sas +46 -0
- package/macros/mp_ds2cards.sas +291 -0
- package/macros/mp_ds2csv.sas +212 -0
- package/macros/mp_ds2ddl.sas +30 -0
- package/macros/mp_ds2fmtds.sas +105 -0
- package/macros/mp_ds2inserts.sas +180 -0
- package/macros/mp_ds2md.sas +103 -0
- package/macros/mp_ds2squeeze.sas +120 -0
- package/macros/mp_dsmeta.sas +110 -0
- package/macros/mp_filtercheck.sas +251 -0
- package/macros/mp_filtergenerate.sas +105 -0
- package/macros/mp_filterstore.sas +254 -0
- package/macros/mp_filtervalidate.sas +105 -0
- package/macros/mp_getcols.sas +67 -0
- package/macros/mp_getconstraints.sas +115 -0
- package/macros/mp_getdbml.sas +335 -0
- package/macros/mp_getddl.sas +400 -0
- package/macros/mp_getformats.sas +124 -0
- package/macros/mp_getmaxvarlengths.sas +130 -0
- package/macros/mp_getpk.sas +281 -0
- package/macros/mp_gsubfile.sas +58 -0
- package/macros/mp_guesspk.sas +332 -0
- package/macros/mp_hashdataset.sas +91 -0
- package/macros/mp_hashdirectory.sas +162 -0
- package/macros/mp_include.sas +106 -0
- package/macros/mp_init.sas +76 -0
- package/macros/mp_jsonout.sas +406 -0
- package/macros/mp_lib2cards.sas +78 -0
- package/macros/mp_lib2inserts.sas +77 -0
- package/macros/mp_loadformat.sas +303 -0
- package/macros/mp_lockanytable.sas +243 -0
- package/macros/mp_lockfilecheck.sas +103 -0
- package/macros/mp_makedata.sas +107 -0
- package/macros/mp_md5.sas +58 -0
- package/macros/mp_perflog.sas +43 -0
- package/macros/mp_prevobs.sas +88 -0
- package/macros/mp_recursivejoin.sas +90 -0
- package/macros/mp_replace.sas +151 -0
- package/macros/mp_reseterror.sas +27 -0
- package/macros/mp_resetoption.sas +39 -0
- package/macros/mp_retainedkey.sas +247 -0
- package/macros/mp_runddl.sas +49 -0
- package/macros/mp_searchcols.sas +103 -0
- package/macros/mp_searchdata.sas +131 -0
- package/macros/mp_setkeyvalue.sas +55 -0
- package/macros/mp_sortinplace.sas +122 -0
- package/macros/mp_stackdiffs.sas +592 -0
- package/macros/mp_storediffs.sas +230 -0
- package/macros/mp_stprequests.sas +74 -0
- package/macros/mp_streamfile.sas +212 -0
- package/macros/mp_testjob.sas +92 -0
- package/macros/mp_testservice.sas +39 -0
- package/macros/mp_testwritespeedlibrary.sas +59 -0
- package/macros/mp_tree.sas +71 -0
- package/macros/mp_unzip.sas +90 -0
- package/macros/mp_updatevarlength.sas +93 -0
- package/macros/mp_validatecol.sas +97 -0
- package/macros/mp_wait4file.sas +35 -0
- package/macros/mp_webin.sas +59 -0
- package/macros/mp_zip.sas +85 -0
- package/package.json +4 -1
- package/scripts/copySASjsCore.ts +36 -0
- package/utils/index.d.ts +1 -2
- package/utils/index.js +2 -3
- package/utils/index.js.map +1 -1
- package/utils/utils.d.ts +1 -0
- package/utils/utils.js +7 -1
- package/utils/utils.js.map +1 -1
- package/utils/getNodeModulePath.d.ts +0 -2
- package/utils/getNodeModulePath.js +0 -83
- package/utils/getNodeModulePath.js.map +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file mp_dictionary.sas
|
|
3
|
+
@brief Creates a portal (libref) into the SQL Dictionary Views
|
|
4
|
+
@details Provide a libref and the macro will create a series of views against
|
|
5
|
+
each view in the special PROC SQL dictionary libref.
|
|
6
|
+
|
|
7
|
+
This is useful if you would like to visualise (navigate) the views in a SAS
|
|
8
|
+
client such as Base SAS, Enterprise Guide, or Studio (or [Data Controller](
|
|
9
|
+
https://datacontroller.io)).
|
|
10
|
+
|
|
11
|
+
It works by extracting the dictionary.dictionaries view into
|
|
12
|
+
YOURLIB.dictionaries, then uses that to create a YOURLIB.{viewName} for every
|
|
13
|
+
other dictionary.view, eg:
|
|
14
|
+
|
|
15
|
+
proc sql;
|
|
16
|
+
create view YOURLIB.columns as select * from dictionary.columns;
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
|
|
20
|
+
libname demo "/lib/directory";
|
|
21
|
+
%mp_dictionary(lib=demo)
|
|
22
|
+
|
|
23
|
+
Or, to just create them in WORK:
|
|
24
|
+
|
|
25
|
+
%mp_dictionary()
|
|
26
|
+
|
|
27
|
+
If you'd just like to browse the dictionary data model, you can also check
|
|
28
|
+
out [this article](https://rawsas.com/dictionary-of-dictionaries/).
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
@param lib= (WORK) The libref in which to create the views
|
|
33
|
+
|
|
34
|
+
<h4> Related Files </h4>
|
|
35
|
+
@li mp_dictionary.test.sas
|
|
36
|
+
|
|
37
|
+
@version 9.2
|
|
38
|
+
@author Allan Bowe
|
|
39
|
+
|
|
40
|
+
**/
|
|
41
|
+
|
|
42
|
+
%macro mp_dictionary(lib=WORK)/*/STORE SOURCE*/;
|
|
43
|
+
%local list i mem;
|
|
44
|
+
proc sql noprint;
|
|
45
|
+
create view &lib..dictionaries as select * from dictionary.dictionaries;
|
|
46
|
+
select distinct memname into: list separated by ' ' from &lib..dictionaries;
|
|
47
|
+
%do i=1 %to %sysfunc(countw(&list,%str( )));
|
|
48
|
+
%let mem=%scan(&list,&i,%str( ));
|
|
49
|
+
create view &lib..&mem as select * from dictionary.&mem;
|
|
50
|
+
%end;
|
|
51
|
+
quit;
|
|
52
|
+
%mend mp_dictionary;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Returns all files and subdirectories within a specified parent
|
|
4
|
+
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
|
|
5
|
+
|
|
6
|
+
Credit for the rename approach:
|
|
7
|
+
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
|
|
11
|
+
%mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX)
|
|
12
|
+
|
|
13
|
+
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
|
|
14
|
+
|
|
15
|
+
%mp_dirlist(fref=MYFREF)
|
|
16
|
+
|
|
17
|
+
@warning In a Unix environment, the existence of a named pipe will cause this
|
|
18
|
+
macro to hang. Therefore this tool should be used with caution in a SAS 9 web
|
|
19
|
+
application, as it can use up all available multibridge sessions if requests
|
|
20
|
+
are resubmitted.
|
|
21
|
+
If anyone finds a way to positively identify a named pipe using SAS (without
|
|
22
|
+
X CMD) do please raise an issue!
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@param [in] path= (%sysfunc(pathname(work))) Path for which to return contents
|
|
26
|
+
@param [in] fref= (0) Provide a DISK engine fileref as an alternative to PATH
|
|
27
|
+
@param [in] maxdepth= (0) Set to a positive integer to indicate the level of
|
|
28
|
+
subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited
|
|
29
|
+
recursion, set to MAX.
|
|
30
|
+
@param [in] showparent= (NO) By default, the initial parent directory is not
|
|
31
|
+
part of the results. Set to YES to include it. For this record only,
|
|
32
|
+
directory=filepath.
|
|
33
|
+
@param [out] outds= (work.mp_dirlist) The output dataset to create
|
|
34
|
+
@param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname
|
|
35
|
+
functions are used to scan all properties - any characters that are not
|
|
36
|
+
valid in a SAS name (v7) are simply stripped, and the table is transposed
|
|
37
|
+
so theat each property is a column and there is one file per row. An
|
|
38
|
+
attempt is made to get all properties whether a file or folder, but some
|
|
39
|
+
files/folders cannot be accessed, and so not all properties can / will be
|
|
40
|
+
populated.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@returns outds contains the following variables:
|
|
44
|
+
- directory (containing folder)
|
|
45
|
+
- file_or_folder (file / folder)
|
|
46
|
+
- filepath (path/to/file.name)
|
|
47
|
+
- filename (just the file name)
|
|
48
|
+
- ext (.extension)
|
|
49
|
+
- msg (system message if any issues)
|
|
50
|
+
- level (depth of folder)
|
|
51
|
+
- OS SPECIFIC variables, if <code>getattrs=</code> is used.
|
|
52
|
+
|
|
53
|
+
<h4> SAS Macros </h4>
|
|
54
|
+
@li mf_existds.sas
|
|
55
|
+
@li mf_getvarlist.sas
|
|
56
|
+
@li mf_wordsinstr1butnotstr2.sas
|
|
57
|
+
@li mp_dropmembers.sas
|
|
58
|
+
|
|
59
|
+
<h4> Related Macros </h4>
|
|
60
|
+
@li mp_dirlist.test.sas
|
|
61
|
+
|
|
62
|
+
@version 9.2
|
|
63
|
+
**/
|
|
64
|
+
|
|
65
|
+
%macro mp_dirlist(path=%sysfunc(pathname(work))
|
|
66
|
+
, fref=0
|
|
67
|
+
, outds=work.mp_dirlist
|
|
68
|
+
, getattrs=NO
|
|
69
|
+
, showparent=NO
|
|
70
|
+
, maxdepth=0
|
|
71
|
+
, level=0 /* The level of recursion to perform. For internal use only. */
|
|
72
|
+
)/*/STORE SOURCE*/;
|
|
73
|
+
%let getattrs=%upcase(&getattrs)XX;
|
|
74
|
+
|
|
75
|
+
/* temp table */
|
|
76
|
+
%local out_ds;
|
|
77
|
+
data;run;
|
|
78
|
+
%let out_ds=%str(&syslast);
|
|
79
|
+
|
|
80
|
+
/* drop main (top) table if it exists */
|
|
81
|
+
%if &level=0 %then %do;
|
|
82
|
+
%mp_dropmembers(%scan(&outds,-1,.), libref=WORK)
|
|
83
|
+
%end;
|
|
84
|
+
|
|
85
|
+
data &out_ds(compress=no
|
|
86
|
+
keep=file_or_folder filepath filename ext msg directory level
|
|
87
|
+
);
|
|
88
|
+
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80
|
|
89
|
+
ext $20 msg $200 foption $16;
|
|
90
|
+
if _n_=1 then call missing(of _all_);
|
|
91
|
+
retain level &level;
|
|
92
|
+
%if &fref=0 %then %do;
|
|
93
|
+
rc = filename(fref, "&path");
|
|
94
|
+
%end;
|
|
95
|
+
%else %do;
|
|
96
|
+
fref="&fref";
|
|
97
|
+
rc=0;
|
|
98
|
+
%end;
|
|
99
|
+
if rc = 0 then do;
|
|
100
|
+
did = dopen(fref);
|
|
101
|
+
if did=0 then do;
|
|
102
|
+
putlog "NOTE: This directory is empty, or does not exist - &path";
|
|
103
|
+
msg=sysmsg();
|
|
104
|
+
put msg;
|
|
105
|
+
put _all_;
|
|
106
|
+
stop;
|
|
107
|
+
end;
|
|
108
|
+
/* attribute is OS-dependent - could be "Directory" or "Directory Name" */
|
|
109
|
+
numopts=doptnum(did);
|
|
110
|
+
do i=1 to numopts;
|
|
111
|
+
foption=doptname(did,i);
|
|
112
|
+
if foption=:'Directory' then i=numopts;
|
|
113
|
+
end;
|
|
114
|
+
directory=dinfo(did,foption);
|
|
115
|
+
rc = filename(fref);
|
|
116
|
+
end;
|
|
117
|
+
else do;
|
|
118
|
+
msg=sysmsg();
|
|
119
|
+
put _all_;
|
|
120
|
+
stop;
|
|
121
|
+
end;
|
|
122
|
+
dnum = dnum(did);
|
|
123
|
+
do i = 1 to dnum;
|
|
124
|
+
filename = dread(did, i);
|
|
125
|
+
filepath=cats(directory,'/',filename);
|
|
126
|
+
rc = filename(fref2,filepath);
|
|
127
|
+
midd=dopen(fref2);
|
|
128
|
+
dmsg=sysmsg();
|
|
129
|
+
if did > 0 then file_or_folder='folder';
|
|
130
|
+
rc=dclose(midd);
|
|
131
|
+
midf=fopen(fref2);
|
|
132
|
+
fmsg=sysmsg();
|
|
133
|
+
if midf > 0 then file_or_folder='file';
|
|
134
|
+
rc=fclose(midf);
|
|
135
|
+
|
|
136
|
+
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
|
|
137
|
+
then file_or_folder='file';
|
|
138
|
+
else if index(fmsg,'Insufficient authorization') then file_or_folder='file';
|
|
139
|
+
else if file_or_folder='' then file_or_folder='locked';
|
|
140
|
+
|
|
141
|
+
if file_or_folder='file' then do;
|
|
142
|
+
ext = prxchange('s/.*\.{1,1}(.*)/$1/', 1, filename);
|
|
143
|
+
if filename = ext then ext = ' ';
|
|
144
|
+
end;
|
|
145
|
+
else do;
|
|
146
|
+
ext='';
|
|
147
|
+
file_or_folder='folder';
|
|
148
|
+
end;
|
|
149
|
+
output;
|
|
150
|
+
end;
|
|
151
|
+
rc = dclose(did);
|
|
152
|
+
%if &showparent=YES and &level=0 %then %do;
|
|
153
|
+
filepath=directory;
|
|
154
|
+
file_or_folder='folder';
|
|
155
|
+
ext='';
|
|
156
|
+
filename=scan(directory,-1,'/\');
|
|
157
|
+
msg='';
|
|
158
|
+
level=&level;
|
|
159
|
+
output;
|
|
160
|
+
%end;
|
|
161
|
+
stop;
|
|
162
|
+
run;
|
|
163
|
+
|
|
164
|
+
%if %substr(&getattrs,1,1)=Y %then %do;
|
|
165
|
+
data &out_ds;
|
|
166
|
+
set &out_ds;
|
|
167
|
+
length infoname infoval $60 fref $8;
|
|
168
|
+
if _n_=1 then call missing(fref);
|
|
169
|
+
rc=filename(fref,filepath);
|
|
170
|
+
drop rc infoname fid i close fref;
|
|
171
|
+
if file_or_folder='file' then do;
|
|
172
|
+
fid=fopen(fref);
|
|
173
|
+
if fid le 0 then do;
|
|
174
|
+
msg=sysmsg();
|
|
175
|
+
putlog "Could not open file:" filepath fid= ;
|
|
176
|
+
sasname='_MCNOTVALID_';
|
|
177
|
+
output;
|
|
178
|
+
end;
|
|
179
|
+
else do i=1 to foptnum(fid);
|
|
180
|
+
infoname=foptname(fid,i);
|
|
181
|
+
infoval=finfo(fid,infoname);
|
|
182
|
+
sasname=compress(infoname, '_', 'adik');
|
|
183
|
+
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
|
184
|
+
if upcase(sasname) ne 'FILENAME' then output;
|
|
185
|
+
end;
|
|
186
|
+
close=fclose(fid);
|
|
187
|
+
end;
|
|
188
|
+
else do;
|
|
189
|
+
fid=dopen(fref);
|
|
190
|
+
if fid le 0 then do;
|
|
191
|
+
msg=sysmsg();
|
|
192
|
+
putlog "Could not open folder:" filepath fid= ;
|
|
193
|
+
sasname='_MCNOTVALID_';
|
|
194
|
+
output;
|
|
195
|
+
end;
|
|
196
|
+
else do i=1 to doptnum(fid);
|
|
197
|
+
infoname=doptname(fid,i);
|
|
198
|
+
infoval=dinfo(fid,infoname);
|
|
199
|
+
sasname=compress(infoname, '_', 'adik');
|
|
200
|
+
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
|
201
|
+
if upcase(sasname) ne 'FILENAME' then output;
|
|
202
|
+
end;
|
|
203
|
+
close=dclose(fid);
|
|
204
|
+
end;
|
|
205
|
+
run;
|
|
206
|
+
proc sort;
|
|
207
|
+
by filepath sasname;
|
|
208
|
+
proc transpose data=&out_ds out=&out_ds(drop=_:);
|
|
209
|
+
id sasname;
|
|
210
|
+
var infoval;
|
|
211
|
+
by filepath file_or_folder filename ext ;
|
|
212
|
+
run;
|
|
213
|
+
%end;
|
|
214
|
+
|
|
215
|
+
data &out_ds;
|
|
216
|
+
set &out_ds(where=(filepath ne ''));
|
|
217
|
+
run;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The above transpose can mean that some updates create additional columns.
|
|
221
|
+
* This necessitates the occasional use of datastep over proc append.
|
|
222
|
+
*/
|
|
223
|
+
%if %mf_existds(&outds) %then %do;
|
|
224
|
+
%local basevars appvars newvars;
|
|
225
|
+
%let basevars=%mf_getvarlist(&outds);
|
|
226
|
+
%let appvars=%mf_getvarlist(&out_ds);
|
|
227
|
+
%let newvars=%length(%mf_wordsinstr1butnotstr2(Str1=&appvars,Str2=&basevars));
|
|
228
|
+
%if &newvars>0 %then %do;
|
|
229
|
+
data &outds;
|
|
230
|
+
set &outds &out_ds;
|
|
231
|
+
run;
|
|
232
|
+
%end;
|
|
233
|
+
%else %do;
|
|
234
|
+
proc append base=&outds data=&out_ds force nowarn;
|
|
235
|
+
run;
|
|
236
|
+
%end;
|
|
237
|
+
%end;
|
|
238
|
+
%else %do;
|
|
239
|
+
proc append base=&outds data=&out_ds;
|
|
240
|
+
run;
|
|
241
|
+
%end;
|
|
242
|
+
|
|
243
|
+
/* recursive call */
|
|
244
|
+
%if &maxdepth>&level or &maxdepth=MAX %then %do;
|
|
245
|
+
data _null_;
|
|
246
|
+
set &out_ds;
|
|
247
|
+
where file_or_folder='folder';
|
|
248
|
+
%if &showparent=YES and &level=0 %then %do;
|
|
249
|
+
if filepath ne directory;
|
|
250
|
+
%end;
|
|
251
|
+
length code $10000;
|
|
252
|
+
code=cats('%nrstr(%mp_dirlist(path=',filepath,",outds=&outds"
|
|
253
|
+
,",getattrs=&getattrs,level=%eval(&level+1),maxdepth=&maxdepth))");
|
|
254
|
+
put code=;
|
|
255
|
+
call execute(code);
|
|
256
|
+
run;
|
|
257
|
+
%end;
|
|
258
|
+
|
|
259
|
+
/* tidy up */
|
|
260
|
+
proc sql;
|
|
261
|
+
drop table &out_ds;
|
|
262
|
+
|
|
263
|
+
%mend mp_dirlist;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Creates a dataset containing distinct _formatted_ values
|
|
4
|
+
@details If no format is supplied, then the original value is used instead.
|
|
5
|
+
There is also a dependency on other macros within the Macro Core library.
|
|
6
|
+
Usage:
|
|
7
|
+
|
|
8
|
+
%mp_distinctfmtvalues(libds=sashelp.class,var=age,outvar=age,outds=test)
|
|
9
|
+
|
|
10
|
+
@param libds input dataset
|
|
11
|
+
@param var variable to get distinct values for
|
|
12
|
+
@param outvar variable to create. Default: `formatted_value`
|
|
13
|
+
@param outds dataset to create. Default: work.mp_distinctfmtvalues
|
|
14
|
+
@param varlen length of variable to create (default 200)
|
|
15
|
+
|
|
16
|
+
@version 9.2
|
|
17
|
+
@author Allan Bowe
|
|
18
|
+
|
|
19
|
+
**/
|
|
20
|
+
|
|
21
|
+
%macro mp_distinctfmtvalues(
|
|
22
|
+
libds=
|
|
23
|
+
,var=
|
|
24
|
+
,outvar=formatted_value
|
|
25
|
+
,outds=work.mp_distinctfmtvalues
|
|
26
|
+
,varlen=2000
|
|
27
|
+
)/*/STORE SOURCE*/;
|
|
28
|
+
|
|
29
|
+
%local fmt vtype;
|
|
30
|
+
%let fmt=%mf_getvarformat(&libds,&var);
|
|
31
|
+
%let vtype=%mf_getvartype(&libds,&var);
|
|
32
|
+
|
|
33
|
+
proc sql;
|
|
34
|
+
create table &outds as
|
|
35
|
+
select distinct
|
|
36
|
+
%if &vtype=C & %trim(&fmt)=%str() %then %do;
|
|
37
|
+
&var
|
|
38
|
+
%end;
|
|
39
|
+
%else %if &vtype=C %then %do;
|
|
40
|
+
put(&var,&fmt)
|
|
41
|
+
%end;
|
|
42
|
+
%else %if %trim(&fmt)=%str() %then %do;
|
|
43
|
+
put(&var,32.)
|
|
44
|
+
%end;
|
|
45
|
+
%else %do;
|
|
46
|
+
put(&var,&fmt)
|
|
47
|
+
%end;
|
|
48
|
+
as &outvar length=&varlen
|
|
49
|
+
from &libds;
|
|
50
|
+
%mend mp_distinctfmtvalues;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Drops tables / views (if they exist) without warnings in the log
|
|
4
|
+
@details Useful for dropping tables when you're not sure they exist, or if
|
|
5
|
+
you are not sure whether they are a dataset or view. Also efficient for
|
|
6
|
+
dropping multiple tables / views.
|
|
7
|
+
|
|
8
|
+
Example usage:
|
|
9
|
+
|
|
10
|
+
proc sql;
|
|
11
|
+
create table data1 as select * from sashelp.class;
|
|
12
|
+
create view view2 as select * from sashelp.class;
|
|
13
|
+
%mp_dropmembers(data1 view2, libref=WORK)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
<h4> SAS Macros </h4>
|
|
17
|
+
@li mf_isblank.sas
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@param [in] list space separated list of datasets / views, WITHOUT libref
|
|
21
|
+
@param [in] libref= (WORK) Note - you can only drop from one library at a time
|
|
22
|
+
@param [in] iftrue= (1=1) Conditionally drop tables, eg if &debug=N
|
|
23
|
+
|
|
24
|
+
@version 9.2
|
|
25
|
+
@author Allan Bowe
|
|
26
|
+
|
|
27
|
+
**/
|
|
28
|
+
|
|
29
|
+
%macro mp_dropmembers(
|
|
30
|
+
list /* space separated list of datasets / views */
|
|
31
|
+
,libref=WORK /* can only drop from a single library at a time */
|
|
32
|
+
,iftrue=%str(1=1)
|
|
33
|
+
)/*/STORE SOURCE*/;
|
|
34
|
+
|
|
35
|
+
%if not(%eval(%unquote(&iftrue))) %then %return;
|
|
36
|
+
|
|
37
|
+
%if %mf_isblank(&list) %then %do;
|
|
38
|
+
%put NOTE: nothing to drop!;
|
|
39
|
+
%return;
|
|
40
|
+
%end;
|
|
41
|
+
|
|
42
|
+
proc datasets lib=&libref nolist;
|
|
43
|
+
delete &list;
|
|
44
|
+
delete &list /mtype=view;
|
|
45
|
+
run;
|
|
46
|
+
%mend mp_dropmembers;
|
|
@@ -0,0 +1,291 @@
|
|
|
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. Includes
|
|
6
|
+
support for large decimals, binary data, PROCESSED_DTTM columns, and
|
|
7
|
+
alternative encoding. If the input dataset is empty, the cards file will
|
|
8
|
+
still be created.
|
|
9
|
+
|
|
10
|
+
Additional support to generate a random sample and max rows.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
|
|
14
|
+
%mp_ds2cards(sashelp.class
|
|
15
|
+
, tgt_ds=work.class
|
|
16
|
+
, cards_file= "C:\temp\class.sas"
|
|
17
|
+
, showlog=NO
|
|
18
|
+
, maxobs=5
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
TODO:
|
|
22
|
+
- labelling the dataset
|
|
23
|
+
- explicity setting a unix LF
|
|
24
|
+
- constraints / indexes etc
|
|
25
|
+
|
|
26
|
+
@param [in] base_ds Should be two level - eg work.blah. This is the table
|
|
27
|
+
that is converted to a cards file.
|
|
28
|
+
@param [in] tgt_ds= Table that the generated cards file would create.
|
|
29
|
+
Optional - if omitted, will be same as BASE_DS.
|
|
30
|
+
@param [out] cards_file= ("%sysfunc(pathname(work))/cardgen.sas") Location in
|
|
31
|
+
which to write the (.sas) cards file
|
|
32
|
+
@param [in] maxobs= (max) To limit output to the first <code>maxobs</code>
|
|
33
|
+
observations, enter an integer here.
|
|
34
|
+
@param [in] random_sample= (NO) Set to YES to generate a random sample of
|
|
35
|
+
data. Can be quite slow.
|
|
36
|
+
@param [in] showlog= (YES) Whether to show generated cards file in the SAS
|
|
37
|
+
log. Valid values:
|
|
38
|
+
@li YES
|
|
39
|
+
@li NO
|
|
40
|
+
@param [in] outencoding= Provide encoding value for file statement (eg utf-8)
|
|
41
|
+
@param [in] append= (NO) If NO then will rebuild the cards file if it already
|
|
42
|
+
exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
|
|
43
|
+
|
|
44
|
+
<h4> Related Macros </h4>
|
|
45
|
+
@li mp_lib2cards.sas
|
|
46
|
+
@li mp_ds2inserts.sas
|
|
47
|
+
@li mp_mdtablewrite.sas
|
|
48
|
+
|
|
49
|
+
@version 9.2
|
|
50
|
+
@author Allan Bowe
|
|
51
|
+
@cond
|
|
52
|
+
**/
|
|
53
|
+
|
|
54
|
+
%macro mp_ds2cards(base_ds, tgt_ds=
|
|
55
|
+
,cards_file="%sysfunc(pathname(work))/cardgen.sas"
|
|
56
|
+
,maxobs=max
|
|
57
|
+
,random_sample=NO
|
|
58
|
+
,showlog=YES
|
|
59
|
+
,outencoding=
|
|
60
|
+
,append=NO
|
|
61
|
+
)/*/STORE SOURCE*/;
|
|
62
|
+
%local i setds nvars;
|
|
63
|
+
|
|
64
|
+
%if not %sysfunc(exist(&base_ds)) %then %do;
|
|
65
|
+
%put %str(WARN)ING: &base_ds does not exist;
|
|
66
|
+
%return;
|
|
67
|
+
%end;
|
|
68
|
+
|
|
69
|
+
%if %index(&base_ds,.)=0 %then %let base_ds=WORK.&base_ds;
|
|
70
|
+
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
|
|
71
|
+
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
|
|
72
|
+
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
|
|
73
|
+
%if ("&append" = "" or "&append" = "NO") %then %let append=;
|
|
74
|
+
%else %let append=mod;
|
|
75
|
+
|
|
76
|
+
/* get varcount */
|
|
77
|
+
%let nvars=0;
|
|
78
|
+
proc sql noprint;
|
|
79
|
+
select count(*) into: nvars from dictionary.columns
|
|
80
|
+
where upcase(libname)="%scan(%upcase(&base_ds),1)"
|
|
81
|
+
and upcase(memname)="%scan(%upcase(&base_ds),2)";
|
|
82
|
+
%if &nvars=0 %then %do;
|
|
83
|
+
%put %str(WARN)ING: Dataset &base_ds has no variables, will not be converted.;
|
|
84
|
+
%return;
|
|
85
|
+
%end;
|
|
86
|
+
|
|
87
|
+
/* get indexes */
|
|
88
|
+
proc sort
|
|
89
|
+
data=sashelp.vindex(
|
|
90
|
+
where=(upcase(libname)="%scan(%upcase(&base_ds),1)"
|
|
91
|
+
and upcase(memname)="%scan(%upcase(&base_ds),2)")
|
|
92
|
+
)
|
|
93
|
+
out=_data_;
|
|
94
|
+
by indxname indxpos;
|
|
95
|
+
run;
|
|
96
|
+
|
|
97
|
+
%local indexes;
|
|
98
|
+
data _null_;
|
|
99
|
+
set &syslast end=last;
|
|
100
|
+
if _n_=1 then call symputx('indexes','(index=(','l');
|
|
101
|
+
by indxname indxpos;
|
|
102
|
+
length vars $32767 nom uni $8;
|
|
103
|
+
retain vars;
|
|
104
|
+
if first.indxname then do;
|
|
105
|
+
idxcnt+1;
|
|
106
|
+
nom='';
|
|
107
|
+
uni='';
|
|
108
|
+
vars=name;
|
|
109
|
+
end;
|
|
110
|
+
else vars=catx(' ',vars,name);
|
|
111
|
+
if last.indxname then do;
|
|
112
|
+
if nomiss='yes' then nom='/nomiss';
|
|
113
|
+
if unique='yes' then uni='/unique';
|
|
114
|
+
call symputx('indexes'
|
|
115
|
+
,catx(' ',symget('indexes'),indxname,'=(',vars,')',nom,uni)
|
|
116
|
+
,'l');
|
|
117
|
+
end;
|
|
118
|
+
if last then call symputx('indexes',cats(symget('indexes'),'))'),'l');
|
|
119
|
+
run;
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
data;run;
|
|
123
|
+
%let setds=&syslast;
|
|
124
|
+
proc sql
|
|
125
|
+
%if %datatyp(&maxobs)=NUMERIC %then %do;
|
|
126
|
+
outobs=&maxobs;
|
|
127
|
+
%end;
|
|
128
|
+
;
|
|
129
|
+
create table &setds as select * from &base_ds
|
|
130
|
+
%if &random_sample=YES %then %do;
|
|
131
|
+
order by ranuni(42)
|
|
132
|
+
%end;
|
|
133
|
+
;
|
|
134
|
+
reset outobs=max;
|
|
135
|
+
create table datalines1 as
|
|
136
|
+
select name,type,length,varnum,format,label from dictionary.columns
|
|
137
|
+
where upcase(libname)="%upcase(%scan(&base_ds,1))"
|
|
138
|
+
and upcase(memname)="%upcase(%scan(&base_ds,2))";
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
Due to long decimals cannot use best. format
|
|
142
|
+
So - use bestd. format and then use character functions to strip trailing
|
|
143
|
+
zeros, if NOT an integer or missing!! Cannot use int() as it upsets
|
|
144
|
+
note2err when there are missings.
|
|
145
|
+
resolved code = ifc( mod(coalesce(VARIABLE,0),1)=0
|
|
146
|
+
,put(VARIABLE,best32.)
|
|
147
|
+
,substrn(put(VARIABLE,bestd32.),1
|
|
148
|
+
,findc(put(VARIABLE,bestd32.),'0','TBK')));
|
|
149
|
+
**/
|
|
150
|
+
|
|
151
|
+
data datalines_2;
|
|
152
|
+
format dataline $32000.;
|
|
153
|
+
set datalines1 (where=(upcase(name) not in
|
|
154
|
+
('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM')));
|
|
155
|
+
if type='num' then dataline=
|
|
156
|
+
cats('ifc(mod(coalesce(',name,',0),1)=0
|
|
157
|
+
,put(',name,',best32.-l)
|
|
158
|
+
,substrn(put(',name,',bestd32.-l),1
|
|
159
|
+
,findc(put(',name,',bestd32.-l),"0","TBK")))');
|
|
160
|
+
/**
|
|
161
|
+
* binary data must be converted, to store in text format. It is identified
|
|
162
|
+
* by the presence of the $HEX keyword in the format.
|
|
163
|
+
*/
|
|
164
|
+
else if upcase(format)=:'$HEX' then
|
|
165
|
+
dataline=cats('put(trim(',name,'),',format,')');
|
|
166
|
+
/**
|
|
167
|
+
* There is no easy way to store line breaks in a cards file.
|
|
168
|
+
* To discuss this, use: https://github.com/sasjs/core/issues/80
|
|
169
|
+
* Removing all nonprintables with kw (keep writeable)
|
|
170
|
+
*/
|
|
171
|
+
else dataline=cats('compress(',name,', ,"kw")');
|
|
172
|
+
run;
|
|
173
|
+
|
|
174
|
+
proc sql noprint;
|
|
175
|
+
select dataline into: datalines separated by ',' from datalines_2;
|
|
176
|
+
|
|
177
|
+
%local
|
|
178
|
+
process_dttm_flg
|
|
179
|
+
valid_from_dttm_flg
|
|
180
|
+
valid_to_dttm_flg
|
|
181
|
+
;
|
|
182
|
+
%let process_dttm_flg = N;
|
|
183
|
+
%let valid_from_dttm_flg = N;
|
|
184
|
+
%let valid_to_dttm_flg = N;
|
|
185
|
+
data _null_;
|
|
186
|
+
set datalines1 ;
|
|
187
|
+
/* build attrib statement */
|
|
188
|
+
if type='char' then type2='$';
|
|
189
|
+
if strip(format) ne '' then format2=cats('format=',format);
|
|
190
|
+
if strip(label) ne '' then label2=cats('label=',quote(trim(label)));
|
|
191
|
+
str1=catx(' ',(put(name,$33.)||'length=')
|
|
192
|
+
,put(cats(type2,length),$7.)||format2,label2);
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
/* Build input statement */
|
|
196
|
+
if upcase(format)=:'$HEX' then type3=':'!!format;
|
|
197
|
+
else if type='char' then type3=':$char.';
|
|
198
|
+
str2=put(name,$33.)||type3;
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if(upcase(name) = "PROCESSED_DTTM") then
|
|
202
|
+
call symputx("process_dttm_flg", "Y", "L");
|
|
203
|
+
if(upcase(name) = "VALID_FROM_DTTM") then
|
|
204
|
+
call symputx("valid_from_dttm_flg", "Y", "L");
|
|
205
|
+
if(upcase(name) = "VALID_TO_DTTM") then
|
|
206
|
+
call symputx("valid_to_dttm_flg", "Y", "L");
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
call symputx(cats("attrib_stmt_", put(_N_, 8.)), str1, "L");
|
|
210
|
+
call symputx(cats("input_stmt_", put(_N_, 8.))
|
|
211
|
+
, ifc(upcase(name) not in
|
|
212
|
+
('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM'), str2, ""), "L");
|
|
213
|
+
run;
|
|
214
|
+
|
|
215
|
+
data _null_;
|
|
216
|
+
file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
|
|
217
|
+
length __attrib $32767;
|
|
218
|
+
if _n_=1 then do;
|
|
219
|
+
put '/**';
|
|
220
|
+
put ' @file';
|
|
221
|
+
put " @brief Datalines for %upcase(%scan(&base_ds,2)) dataset";
|
|
222
|
+
put " @details Generated by %nrstr(%%)mp_ds2cards()";
|
|
223
|
+
put " Source: https://github.com/sasjs/core";
|
|
224
|
+
put ' @cond ';
|
|
225
|
+
put '**/';
|
|
226
|
+
put "data &tgt_ds &indexes;";
|
|
227
|
+
put "attrib ";
|
|
228
|
+
%do i = 1 %to &nvars;
|
|
229
|
+
__attrib=symget("attrib_stmt_&i");
|
|
230
|
+
put __attrib;
|
|
231
|
+
%end;
|
|
232
|
+
put ";";
|
|
233
|
+
|
|
234
|
+
%if &process_dttm_flg. eq Y %then %do;
|
|
235
|
+
put 'retain PROCESSED_DTTM %sysfunc(datetime());';
|
|
236
|
+
%end;
|
|
237
|
+
%if &valid_from_dttm_flg. eq Y %then %do;
|
|
238
|
+
put 'retain VALID_FROM_DTTM &low_date;';
|
|
239
|
+
%end;
|
|
240
|
+
%if &valid_to_dttm_flg. eq Y %then %do;
|
|
241
|
+
put 'retain VALID_TO_DTTM &high_date;';
|
|
242
|
+
%end;
|
|
243
|
+
if __nobs=0 then do;
|
|
244
|
+
put 'call missing(of _all_);/* avoid uninitialised notes */';
|
|
245
|
+
put 'stop;';
|
|
246
|
+
put 'run;';
|
|
247
|
+
end;
|
|
248
|
+
else do;
|
|
249
|
+
put "infile cards dsd;";
|
|
250
|
+
put "input ";
|
|
251
|
+
%do i = 1 %to &nvars.;
|
|
252
|
+
%if(%length(&&input_stmt_&i..)) %then
|
|
253
|
+
put " &&input_stmt_&i..";
|
|
254
|
+
;
|
|
255
|
+
%end;
|
|
256
|
+
put ";";
|
|
257
|
+
put 'missing a b c d e f g h i j k l m n o p q r s t u v w x y z _;';
|
|
258
|
+
put "datalines4;";
|
|
259
|
+
end;
|
|
260
|
+
end;
|
|
261
|
+
set &setds end=__lastobs nobs=__nobs;
|
|
262
|
+
/* remove all formats for write purposes - some have long underlying decimals */
|
|
263
|
+
format _numeric_ best30.29;
|
|
264
|
+
length __dataline $32767;
|
|
265
|
+
__dataline=catq('cqtmb',&datalines);
|
|
266
|
+
put __dataline;
|
|
267
|
+
if __lastobs then do;
|
|
268
|
+
put ';;;;';
|
|
269
|
+
put 'run;';
|
|
270
|
+
put '/** @endcond **/';
|
|
271
|
+
stop;
|
|
272
|
+
end;
|
|
273
|
+
run;
|
|
274
|
+
proc sql;
|
|
275
|
+
drop table &setds;
|
|
276
|
+
quit;
|
|
277
|
+
|
|
278
|
+
%if &showlog=YES %then %do;
|
|
279
|
+
data _null_;
|
|
280
|
+
infile &cards_file lrecl=32767;
|
|
281
|
+
input;
|
|
282
|
+
put _infile_;
|
|
283
|
+
run;
|
|
284
|
+
%end;
|
|
285
|
+
|
|
286
|
+
%put NOTE: CARDS FILE SAVED IN:;
|
|
287
|
+
%put NOTE-;%put NOTE-;
|
|
288
|
+
%put NOTE- %sysfunc(dequote(&cards_file.));
|
|
289
|
+
%put NOTE-;%put NOTE-;
|
|
290
|
+
%mend mp_ds2cards;
|
|
291
|
+
/** @endcond **/
|