@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.
Files changed (144) hide show
  1. package/fs/internal/helper.js +19 -24
  2. package/fs/internal/helper.js.map +1 -1
  3. package/macros/mf_abort.sas +27 -0
  4. package/macros/mf_dedup.sas +53 -0
  5. package/macros/mf_deletefile.sas +31 -0
  6. package/macros/mf_existds.sas +30 -0
  7. package/macros/mf_existfeature.sas +59 -0
  8. package/macros/mf_existfileref.sas +33 -0
  9. package/macros/mf_existfunction.sas +37 -0
  10. package/macros/mf_existvar.sas +39 -0
  11. package/macros/mf_existvarlist.sas +56 -0
  12. package/macros/mf_fmtdttm.sas +42 -0
  13. package/macros/mf_getapploc.sas +78 -0
  14. package/macros/mf_getattrc.sas +34 -0
  15. package/macros/mf_getattrn.sas +34 -0
  16. package/macros/mf_getengine.sas +54 -0
  17. package/macros/mf_getfilesize.sas +64 -0
  18. package/macros/mf_getfmtlist.sas +61 -0
  19. package/macros/mf_getfmtname.sas +44 -0
  20. package/macros/mf_getkeyvalue.sas +32 -0
  21. package/macros/mf_getplatform.sas +75 -0
  22. package/macros/mf_getquotedstr.sas +57 -0
  23. package/macros/mf_getschema.sas +43 -0
  24. package/macros/mf_getuniquefileref.sas +60 -0
  25. package/macros/mf_getuniquelibref.sas +57 -0
  26. package/macros/mf_getuniquename.sas +22 -0
  27. package/macros/mf_getuser.sas +44 -0
  28. package/macros/mf_getvalue.sas +36 -0
  29. package/macros/mf_getvarcount.sas +49 -0
  30. package/macros/mf_getvarformat.sas +72 -0
  31. package/macros/mf_getvarlen.sas +56 -0
  32. package/macros/mf_getvarlist.sas +74 -0
  33. package/macros/mf_getvarnum.sas +58 -0
  34. package/macros/mf_getvartype.sas +52 -0
  35. package/macros/mf_getxengine.sas +43 -0
  36. package/macros/mf_increment.sas +29 -0
  37. package/macros/mf_isblank.sas +27 -0
  38. package/macros/mf_isdir.sas +34 -0
  39. package/macros/mf_isint.sas +36 -0
  40. package/macros/mf_islibds.sas +40 -0
  41. package/macros/mf_loc.sas +29 -0
  42. package/macros/mf_mkdir.sas +67 -0
  43. package/macros/mf_mval.sas +19 -0
  44. package/macros/mf_nobs.sas +26 -0
  45. package/macros/mf_trimstr.sas +50 -0
  46. package/macros/mf_uid.sas +21 -0
  47. package/macros/mf_verifymacvars.sas +72 -0
  48. package/macros/mf_wordsinstr1andstr2.sas +53 -0
  49. package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
  50. package/macros/mf_writefile.sas +67 -0
  51. package/macros/mp_abort.sas +301 -0
  52. package/macros/mp_appendfile.sas +57 -0
  53. package/macros/mp_applyformats.sas +181 -0
  54. package/macros/mp_assert.sas +56 -0
  55. package/macros/mp_assertcols.sas +145 -0
  56. package/macros/mp_assertcolvals.sas +168 -0
  57. package/macros/mp_assertdsobs.sas +121 -0
  58. package/macros/mp_assertscope.sas +147 -0
  59. package/macros/mp_base64copy.sas +117 -0
  60. package/macros/mp_binarycopy.sas +79 -0
  61. package/macros/mp_chop.sas +194 -0
  62. package/macros/mp_cleancsv.sas +78 -0
  63. package/macros/mp_cntlout.sas +85 -0
  64. package/macros/mp_copyfolder.sas +84 -0
  65. package/macros/mp_coretable.sas +73 -0
  66. package/macros/mp_createconstraints.sas +70 -0
  67. package/macros/mp_createwebservice.sas +32 -0
  68. package/macros/mp_csv2ds.sas +140 -0
  69. package/macros/mp_deleteconstraints.sas +52 -0
  70. package/macros/mp_deletefolder.sas +70 -0
  71. package/macros/mp_dictionary.sas +52 -0
  72. package/macros/mp_dirlist.sas +263 -0
  73. package/macros/mp_distinctfmtvalues.sas +50 -0
  74. package/macros/mp_dropmembers.sas +46 -0
  75. package/macros/mp_ds2cards.sas +291 -0
  76. package/macros/mp_ds2csv.sas +212 -0
  77. package/macros/mp_ds2ddl.sas +30 -0
  78. package/macros/mp_ds2fmtds.sas +105 -0
  79. package/macros/mp_ds2inserts.sas +180 -0
  80. package/macros/mp_ds2md.sas +103 -0
  81. package/macros/mp_ds2squeeze.sas +120 -0
  82. package/macros/mp_dsmeta.sas +110 -0
  83. package/macros/mp_filtercheck.sas +251 -0
  84. package/macros/mp_filtergenerate.sas +105 -0
  85. package/macros/mp_filterstore.sas +254 -0
  86. package/macros/mp_filtervalidate.sas +105 -0
  87. package/macros/mp_getcols.sas +67 -0
  88. package/macros/mp_getconstraints.sas +115 -0
  89. package/macros/mp_getdbml.sas +335 -0
  90. package/macros/mp_getddl.sas +400 -0
  91. package/macros/mp_getformats.sas +124 -0
  92. package/macros/mp_getmaxvarlengths.sas +130 -0
  93. package/macros/mp_getpk.sas +281 -0
  94. package/macros/mp_gsubfile.sas +58 -0
  95. package/macros/mp_guesspk.sas +332 -0
  96. package/macros/mp_hashdataset.sas +91 -0
  97. package/macros/mp_hashdirectory.sas +162 -0
  98. package/macros/mp_include.sas +106 -0
  99. package/macros/mp_init.sas +76 -0
  100. package/macros/mp_jsonout.sas +406 -0
  101. package/macros/mp_lib2cards.sas +78 -0
  102. package/macros/mp_lib2inserts.sas +77 -0
  103. package/macros/mp_loadformat.sas +303 -0
  104. package/macros/mp_lockanytable.sas +243 -0
  105. package/macros/mp_lockfilecheck.sas +103 -0
  106. package/macros/mp_makedata.sas +107 -0
  107. package/macros/mp_md5.sas +58 -0
  108. package/macros/mp_perflog.sas +43 -0
  109. package/macros/mp_prevobs.sas +88 -0
  110. package/macros/mp_recursivejoin.sas +90 -0
  111. package/macros/mp_replace.sas +151 -0
  112. package/macros/mp_reseterror.sas +27 -0
  113. package/macros/mp_resetoption.sas +39 -0
  114. package/macros/mp_retainedkey.sas +247 -0
  115. package/macros/mp_runddl.sas +49 -0
  116. package/macros/mp_searchcols.sas +103 -0
  117. package/macros/mp_searchdata.sas +131 -0
  118. package/macros/mp_setkeyvalue.sas +55 -0
  119. package/macros/mp_sortinplace.sas +122 -0
  120. package/macros/mp_stackdiffs.sas +592 -0
  121. package/macros/mp_storediffs.sas +230 -0
  122. package/macros/mp_stprequests.sas +74 -0
  123. package/macros/mp_streamfile.sas +212 -0
  124. package/macros/mp_testjob.sas +92 -0
  125. package/macros/mp_testservice.sas +39 -0
  126. package/macros/mp_testwritespeedlibrary.sas +59 -0
  127. package/macros/mp_tree.sas +71 -0
  128. package/macros/mp_unzip.sas +90 -0
  129. package/macros/mp_updatevarlength.sas +93 -0
  130. package/macros/mp_validatecol.sas +97 -0
  131. package/macros/mp_wait4file.sas +35 -0
  132. package/macros/mp_webin.sas +59 -0
  133. package/macros/mp_zip.sas +85 -0
  134. package/package.json +4 -1
  135. package/scripts/copySASjsCore.ts +36 -0
  136. package/utils/index.d.ts +1 -2
  137. package/utils/index.js +2 -3
  138. package/utils/index.js.map +1 -1
  139. package/utils/utils.d.ts +1 -0
  140. package/utils/utils.js +7 -1
  141. package/utils/utils.js.map +1 -1
  142. package/utils/getNodeModulePath.d.ts +0 -2
  143. package/utils/getNodeModulePath.js +0 -83
  144. package/utils/getNodeModulePath.js.map +0 -1
@@ -0,0 +1,247 @@
1
+ /**
2
+ @file
3
+ @brief Generate and apply retained key values to a staging table
4
+ @details This macro will populate a staging table with a Retained Key based on
5
+ a business key and a base (target) table.
6
+
7
+ Definition of retained key ([source](
8
+ http://bukhantsov.org/2012/04/what-is-data-vault/)):
9
+
10
+ > The retained key is a key which is mapped to business key one-to-one. In
11
+ > comparison, the surrogate key includes time and there can be many surrogate
12
+ > keys corresponding to one business key. This explains the name of the key,
13
+ > it is retained with insertion of a new version of a row while surrogate key
14
+ > is increasing.
15
+
16
+ This macro is designed to be used as part of a wider load / ETL process (such
17
+ as the one in [Data Controller for SAS](https://datacontroller.io)).
18
+
19
+ Specifically, the macro assumes that the base table has already been 'locked'
20
+ (eg with the mp_lockanytable.sas macro) prior to invocation. Also, several
21
+ tables are assumed to exist (names are configurable):
22
+
23
+ @li work.staging_table - the staged data, minus the retained key element
24
+ @li permlib.base_table - the target table to be loaded (**not** loaded by this
25
+ macro)
26
+ @li permlib.maxkeytable - optional, used to store load metaadata.
27
+ The definition is available by running mp_coretable.sas as follows:
28
+ `mp_coretable(MAXKEYTABLE)`.
29
+ @li permlib.locktable - Necessary if maxkeytable is being populated. The
30
+ definition is available by running mp_coretable.sas as follows:
31
+ `mp_coretable(LOCKTABLE)`.
32
+
33
+
34
+ @param [in] base_lib= (WORK) Libref of the base (target) table.
35
+ @param [in] base_dsn= (BASETABLE) Name of the base (target) table.
36
+ @param [in] append_lib= (WORK) Libref of the staging table
37
+ @param [in] append_dsn= (APPENDTABLE) Name of the staging table
38
+ @param [in] retained_key= (DEFAULT_RK) Name of RK to generate (should exist on
39
+ base table)
40
+ @param [in] business_key= (PK1 PK2) Business key against which to generate
41
+ RK values. Should be unique and not null on the staging table.
42
+ @param [in] check_uniqueness=(NO) Set to yes to perform a uniqueness check.
43
+ Recommended if there is a chance that the staging data is not unique on the
44
+ business key.
45
+ @param [in] maxkeytable= (0) Provide a maxkeytable libds reference here, to
46
+ store load metadata (maxkey val, load time). Set to zero if metadata is not
47
+ required, eg, when preparing a 'dummy' load. Structure is described above.
48
+ See below for sample data.
49
+ |KEYTABLE:$32.|KEYCOLUMN:$32.|MAX_KEY:best.|PROCESSED_DTTM:E8601DT26.6|
50
+ |---|---|---|---|
51
+ |`DC487173.MPE_SELECTBOX `|`SELECTBOX_RK `|`55 `|`1950427787.8 `|
52
+ |`DC487173.MPE_FILTERANYTABLE `|`filter_rk `|`14 `|`1951053886.8 `|
53
+ @param [in] locktable= (0) If updating the maxkeytable, provide the libds
54
+ reference to the lock table (per mp_lockanytable.sas macro)
55
+ @param [in] filter_str= Apply a filter - useful for SCD2 or BITEMPORAL loads.
56
+ Example: `filter_str=%str( (where=( &now < &tech_to)) )`
57
+ @param [out] outds= (WORK.APPEND) Output table (staging table + retained key)
58
+
59
+ <h4> SAS Macros </h4>
60
+ @li mf_existvar.sas
61
+ @li mf_fmtdttm.sas
62
+ @li mf_getquotedstr.sas
63
+ @li mf_getuniquename.sas
64
+ @li mf_nobs.sas
65
+ @li mp_abort.sas
66
+ @li mp_lockanytable.sas
67
+
68
+ <h4> Related Macros </h4>
69
+ @li mp_filterstore.sas
70
+ @li mp_retainedkey.test.sas
71
+
72
+ @version 9.2
73
+
74
+ **/
75
+
76
+ %macro mp_retainedkey(
77
+ base_lib=WORK
78
+ ,base_dsn=BASETABLE
79
+ ,append_lib=WORK
80
+ ,append_dsn=APPENDTABLE
81
+ ,retained_key=DEFAULT_RK
82
+ ,business_key= PK1 PK2
83
+ ,check_uniqueness=NO
84
+ ,maxkeytable=0
85
+ ,locktable=0
86
+ ,outds=WORK.APPEND
87
+ ,filter_str=
88
+ );
89
+ %put &sysmacroname entry vars:;
90
+ %put _local_;
91
+
92
+ %local base_libds app_libds key_field check maxkey idx_pk newkey_cnt iserr
93
+ msg x tempds1 tempds2 comma_pk appnobs checknobs dropvar tempvar idx_val;
94
+ %let base_libds=%upcase(&base_lib..&base_dsn);
95
+ %let app_libds=%upcase(&append_lib..&append_dsn);
96
+ %let tempds1=%mf_getuniquename();
97
+ %let tempds2=%mf_getuniquename();
98
+ %let comma_pk=%mf_getquotedstr(in_str=%str(&business_key),dlm=%str(,),quote=);
99
+ %let outds=%sysfunc(ifc(%index(&outds,.)=0,work.&outds,&outds));
100
+ /* validation checks */
101
+ %let iserr=0;
102
+ %if &syscc>0 %then %do;
103
+ %let iserr=1;
104
+ %let msg=%str(SYSCC=&syscc on macro entry);
105
+ %end;
106
+ %else %if %sysfunc(exist(&base_libds))=0 %then %do;
107
+ %let iserr=1;
108
+ %let msg=%str(Base LIBDS (&base_libds) expected but NOT FOUND);
109
+ %end;
110
+ %else %if %sysfunc(exist(&app_libds))=0 %then %do;
111
+ %let iserr=1;
112
+ %let msg=%str(Append LIBDS (&app_libds) expected but NOT FOUND);
113
+ %end;
114
+ %else %if &maxkeytable ne 0 and %sysfunc(exist(&maxkeytable))=0 %then %do;
115
+ %let iserr=1;
116
+ %let msg=%str(Maxkeytable (&maxkeytable) expected but NOT FOUND);
117
+ %end;
118
+ %else %if &maxkeytable ne 0 and %sysfunc(exist(&locktable))=0 %then %do;
119
+ %let iserr=1;
120
+ %let msg=%str(Locktable (&locktable) expected but NOT FOUND);
121
+ %end;
122
+ %else %if %length(&business_key)=0 %then %do;
123
+ %let iserr=1;
124
+ %let msg=%str(Business key (&business_key) expected but NOT FOUND);
125
+ %end;
126
+
127
+ %do x=1 %to %sysfunc(countw(&business_key));
128
+ /* check business key values exist */
129
+ %let key_field=%scan(&business_key,&x,%str( ));
130
+ %if not %mf_existvar(&app_libds,&key_field) %then %do;
131
+ %let iserr=1;
132
+ %let msg=Business key (&key_field) not found on &app_libds!;
133
+ %goto err;
134
+ %end;
135
+ %else %if not %mf_existvar(&base_libds,&key_field) %then %do;
136
+ %let iserr=1;
137
+ %let msg=Business key (&key_field) not found on &base_libds!;
138
+ %goto err;
139
+ %end;
140
+ %end;
141
+ %err:
142
+ %if &iserr=1 %then %do;
143
+ /* err case so first perform an unlock of the base table before exiting */
144
+ %mp_lockanytable(
145
+ UNLOCK,lib=&base_lib,ds=&base_dsn,ref=%superq(msg),ctl_ds=&locktable
146
+ )
147
+ %end;
148
+ %mp_abort(iftrue=(&iserr=1),mac=mp_retainedkey,msg=%superq(msg))
149
+
150
+ proc sql noprint;
151
+ select sum(max(&retained_key),0) into: maxkey from &base_libds;
152
+
153
+ /**
154
+ * get base table RK and bus field values for lookup
155
+ */
156
+ proc sql noprint;
157
+ create table &tempds1 as
158
+ select distinct &comma_pk,&retained_key
159
+ from &base_libds &filter_str
160
+ order by &comma_pk,&retained_key;
161
+
162
+ %if &check_uniqueness=YES %then %do;
163
+ select count(*) into:checknobs
164
+ from (select distinct &comma_pk from &app_libds);
165
+ select count(*) into: appnobs from &app_libds; /* might be view */
166
+ %if &checknobs ne &appnobs %then %do;
167
+ %let msg=Source table &app_libds is not unique on (&business_key);
168
+ %let iserr=1;
169
+ %end;
170
+ %end;
171
+ %if &iserr=1 %then %do;
172
+ /* err case so first perform an unlock of the base table before exiting */
173
+ %mp_lockanytable(
174
+ UNLOCK,lib=&base_lib,ds=&base_dsn,ref=%superq(msg),ctl_ds=&locktable
175
+ )
176
+ %end;
177
+ %mp_abort(iftrue= (&iserr=1),mac=mp_retainedkey,msg=%superq(msg))
178
+
179
+ %if %mf_existvar(&app_libds,&retained_key)
180
+ %then %let dropvar=(drop=&retained_key);
181
+
182
+ /* prepare interim table with retained key populated for matching keys */
183
+ proc sql noprint;
184
+ create table &tempds2 as
185
+ select b.&retained_key, a.*
186
+ from &app_libds &dropvar a
187
+ left join &tempds1 b
188
+ on 1
189
+ %do idx_pk=1 %to %sysfunc(countw(&business_key));
190
+ %let idx_val=%scan(&business_key,&idx_pk);
191
+ and a.&idx_val=b.&idx_val
192
+ %end;
193
+ order by &retained_key;
194
+
195
+ /* identify the number of entries without retained keys (new records) */
196
+ select count(*) into: newkey_cnt
197
+ from &tempds2
198
+ where missing(&retained_key);
199
+ quit;
200
+
201
+ /**
202
+ * Update maxkey table if link provided
203
+ */
204
+ %if &maxkeytable ne 0 %then %do;
205
+ proc sql noprint;
206
+ select count(*) into: check from &maxkeytable
207
+ where upcase(keytable)="&base_libds";
208
+
209
+ %mp_lockanytable(LOCK
210
+ ,lib=%scan(&maxkeytable,1,.)
211
+ ,ds=%scan(&maxkeytable,2,.)
212
+ ,ref=Updating maxkeyvalues with mp_retainedkey
213
+ ,ctl_ds=&locktable
214
+ )
215
+ proc sql;
216
+ %if &check=0 %then %do;
217
+ insert into &maxkeytable
218
+ set keytable="&base_libds"
219
+ ,keycolumn="&retained_key"
220
+ ,max_key=%eval(&maxkey+&newkey_cnt)
221
+ ,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
222
+ %end;
223
+ %else %do;
224
+ update &maxkeytable
225
+ set max_key=%eval(&maxkey+&newkey_cnt)
226
+ ,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
227
+ where keytable="&base_libds";
228
+ %end;
229
+ %mp_lockanytable(UNLOCK
230
+ ,lib=%scan(&maxkeytable,1,.)
231
+ ,ds=%scan(&maxkeytable,2,.)
232
+ ,ref=Updating maxkeyvalues with maxkey=%eval(&maxkey+&newkey_cnt)
233
+ ,ctl_ds=&locktable
234
+ )
235
+ %end;
236
+
237
+ /* fill in the missing retained key values */
238
+ %let tempvar=%mf_getuniquename();
239
+ data &outds(drop=&tempvar);
240
+ retain &tempvar %eval(&maxkey+1);
241
+ set &tempds2;
242
+ if &retained_key =. then &retained_key=&tempvar;
243
+ &tempvar=&tempvar+1;
244
+ run;
245
+
246
+ %mend mp_retainedkey;
247
+
@@ -0,0 +1,49 @@
1
+ /**
2
+ @file mp_runddl.sas
3
+ @brief An opinionated way to execute DDL files in SAS.
4
+ @details When delivering projects there should be seperation between the DDL
5
+ used to generate the tables and the sample data used to populate them.
6
+
7
+ This macro expects certain folder structure - eg:
8
+
9
+ rootlib
10
+ |-- LIBREF1
11
+ | |__ mytable.ddl
12
+ | |__ someothertable.ddl
13
+ |-- LIBREF2
14
+ | |__ table1.ddl
15
+ | |__ table2.ddl
16
+ |-- LIBREF3
17
+ |__ table3.ddl
18
+ |__ table4.ddl
19
+
20
+ Only files with the .ddl suffix are executed. The parent folder name is used
21
+ as the libref.
22
+ Files should NOT contain the `proc sql` statement - this is to prevent
23
+ statements being executed if there is an err condition.
24
+
25
+ Usage:
26
+
27
+ %mp_runddl(/some/rootlib) * execute all libs ;
28
+
29
+ %mp_runddl(/some/rootlib, inc=LIBREF1 LIBREF2) * include only these libs;
30
+
31
+ %mp_runddl(/some/rootlib, exc=LIBREF3) * same as above ;
32
+
33
+
34
+ @param path location of the DDL folder structure
35
+ @param inc= list of librefs to include
36
+ @param exc= list of librefs to exclude (takes precedence over inc=)
37
+
38
+ @version 9.3
39
+ @author Allan Bowe
40
+ @source https://github.com/sasjs/core
41
+
42
+ **/
43
+
44
+ %macro mp_runddl(path, inc=, exc=
45
+ )/*/STORE SOURCE*/;
46
+
47
+
48
+
49
+ %mend mp_runddl;
@@ -0,0 +1,103 @@
1
+ /**
2
+ @file mp_searchcols.sas
3
+ @brief Searches all columns in a library
4
+ @details
5
+ Scans a set of libraries and creates a dataset containing all source tables
6
+ containing one or more of a particular set of columns
7
+
8
+ Usage:
9
+
10
+ %mp_searchcols(libs=sashelp work, cols=name sex age)
11
+
12
+ @param libs=(SASHELP) Space separated list of libraries to search for columns
13
+ @param cols= Space separated list of column names to search for (not case
14
+ sensitive)
15
+ @param outds=(mp_searchcols) the table to create with the results. Will have
16
+ one line per table match.
17
+ @param match=(ANY) The match type. Valid values:
18
+ @li ANY - The table contains at least one of the columns
19
+ @li WILD - The table contains a column with a name that partially matches
20
+
21
+ @version 9.2
22
+ @author Allan Bowe
23
+ **/
24
+
25
+ %macro mp_searchcols(libs=sashelp
26
+ ,cols=
27
+ ,outds=mp_searchcols
28
+ ,match=ANY
29
+ )/*/STORE SOURCE*/;
30
+
31
+ %put &sysmacroname process began at %sysfunc(datetime(),datetime19.);
32
+
33
+ /* get the list of tables in the library */
34
+ proc sql;
35
+ create table _data_ as
36
+ select distinct upcase(libname) as libname
37
+ , upcase(memname) as memname
38
+ , upcase(name) as name
39
+ from dictionary.columns
40
+ %if %sysevalf(%superq(libs)=,boolean)=0 %then %do;
41
+ where upcase(libname) in ("IMPOSSIBLE",
42
+ %local x;
43
+ %do x=1 %to %sysfunc(countw(&libs));
44
+ "%upcase(%scan(&libs,&x))"
45
+ %end;
46
+ )
47
+ %end;
48
+ order by 1,2,3;
49
+
50
+ %local tempds;
51
+ %let tempds=&syslast;
52
+ data &outds;
53
+ set &tempds;
54
+ length cols matchcols $32767;
55
+ cols=upcase(symget('cols'));
56
+ colcount=countw(cols);
57
+ by libname memname name;
58
+ if _n_=1 then do;
59
+ putlog "Searching libs: &libs";
60
+ putlog "Searching cols: " cols;
61
+ end;
62
+ if first.memname then do;
63
+ sumcols=0;
64
+ retain matchcols;
65
+ matchcols='';
66
+ end;
67
+ %if &match=ANY %then %do;
68
+ if findw(cols,name,,'spit') then do;
69
+ sumcols+1;
70
+ matchcols=cats(matchcols)!!' '!!cats(name);
71
+ end;
72
+ %end;
73
+ %else %if &match=WILD %then %do;
74
+ if _n_=1 then do;
75
+ retain wcount;
76
+ wcount=countw(cols);
77
+ drop wcount;
78
+ end;
79
+ do i=1 to wcount;
80
+ length curword $32;
81
+ curword=scan(cols,i,' ');
82
+ drop curword;
83
+ if index(name,cats(curword)) then do;
84
+ sumcols+1;
85
+ matchcols=cats(matchcols)!!' '!!cats(curword);
86
+ end;
87
+ end;
88
+ %end;
89
+
90
+ if last.memname then do;
91
+ if sumcols>0 then output;
92
+ if sumcols=colcount then putlog "Full Match: " libname memname;
93
+ end;
94
+ keep libname memname sumcols matchcols;
95
+ run;
96
+
97
+ proc sort; by descending sumcols memname libname; run;
98
+
99
+ proc sql;
100
+ drop table &tempds;
101
+ %put &sysmacroname process finished at %sysfunc(datetime(),datetime19.);
102
+
103
+ %mend mp_searchcols;
@@ -0,0 +1,131 @@
1
+ /**
2
+ @file
3
+ @brief Searches all data in a library
4
+ @details
5
+ Scans an entire library and creates a copy of any table
6
+ containing a specific string OR numeric value. Only
7
+ matching records are written out.
8
+ If both a string and numval are provided, the string
9
+ will take precedence.
10
+
11
+ Usage:
12
+
13
+ %mp_searchdata(lib=sashelp, string=Jan)
14
+ %mp_searchdata(lib=sashelp, ds=bird, numval=1)
15
+ %mp_searchdata(lib=sashelp, ds=class, string=l,outobs=5)
16
+
17
+
18
+ Outputs zero or more tables to an MPSEARCH library with specific records.
19
+
20
+ @param [in] lib= The libref to search (should be already assigned)
21
+ @param [in] ds= The dataset to search (leave blank to search entire library)
22
+ @param [in] string= String value to search (case sensitive, can be partial)
23
+ @param [in] numval= Numeric value to search (must be exact)
24
+ @param [out] outloc= (0) Optionally specify the directory in which to
25
+ create the the output datasets with matching rows. By default it will
26
+ write them to a temporary subdirectory within the WORK folder.
27
+ @param [out] outlib= (MPSEARCH) Assign a different libref to the output
28
+ library containing the matching datasets / records
29
+ @param [in] outobs= set to a positive integer to restrict the number of
30
+ observations
31
+ @param [in] filter_text= (1=1) Add a (valid) filter clause to further filter
32
+ the results.
33
+
34
+ <h4> SAS Macros </h4>
35
+ @li mf_getuniquename.sas
36
+ @li mf_getvarlist.sas
37
+ @li mf_getvartype.sas
38
+ @li mf_mkdir.sas
39
+ @li mf_nobs.sas
40
+
41
+ @version 9.2
42
+ @author Allan Bowe
43
+ **/
44
+
45
+ %macro mp_searchdata(lib=
46
+ ,ds=
47
+ ,string= /* the query will use a contains (?) operator */
48
+ ,numval= /* numeric must match exactly */
49
+ ,outloc=0
50
+ ,outlib=MPSEARCH
51
+ ,outobs=-1
52
+ ,filter_text=%str(1=1)
53
+ )/*/STORE SOURCE*/;
54
+
55
+ %local table_list table table_num table colnum col start_tm check_tm vars type
56
+ coltype;
57
+ %put process began at %sysfunc(datetime(),datetime19.);
58
+
59
+ %if &syscc ge 4 %then %do;
60
+ %put %str(WAR)NING: SYSCC=&syscc on macro entry;
61
+ %return;
62
+ %end;
63
+
64
+ %if &string = %then %let type=N;
65
+ %else %let type=C;
66
+
67
+ %if "&outloc"="0" %then %do;
68
+ %let outloc=%sysfunc(pathname(work))/%mf_getuniquename();
69
+ %end;
70
+
71
+ %mf_mkdir(&outloc)
72
+ libname &outlib "&outloc";
73
+
74
+ /* get the list of tables in the library */
75
+ proc sql noprint;
76
+ select distinct memname into: table_list separated by ' '
77
+ from dictionary.tables
78
+ where upcase(libname)="%upcase(&lib)"
79
+ %if &ds ne %then %do;
80
+ and upcase(memname)=%upcase("&ds")
81
+ %end;
82
+ ;
83
+ /* check that we have something to check */
84
+ %if %length(&table_list)=0 %then %put library &lib contains no tables!;
85
+ /* loop through each table */
86
+ %else %do table_num=1 %to %sysfunc(countw(&table_list,%str( )));
87
+ %let table=%scan(&table_list,&table_num,%str( ));
88
+ %let vars=%mf_getvarlist(&lib..&table);
89
+ %if %length(&vars)=0 %then %do;
90
+ %put NO COLUMNS IN &lib..&table! This will be skipped.;
91
+ %end;
92
+ %else %do;
93
+ %let check_tm=%sysfunc(datetime());
94
+ /* prep input */
95
+ data &outlib..&table;
96
+ set &lib..&table;
97
+ where %unquote(&filter_text) and ( 0
98
+ /* loop through columns */
99
+ %do colnum=1 %to %sysfunc(countw(&vars,%str( )));
100
+ %let col=%scan(&vars,&colnum,%str( ));
101
+ %let coltype=%mf_getvartype(&lib..&table,&col);
102
+ %if &type=C and &coltype=C %then %do;
103
+ /* if a char column, see if it contains the string */
104
+ or ("&col"n ? "&string")
105
+ %end;
106
+ %else %if &type=N and &coltype=N %then %do;
107
+ /* if numeric match exactly */
108
+ or ("&col"n = &numval)
109
+ %end;
110
+ %end;
111
+ );
112
+ %if &outobs>-1 %then %do;
113
+ if _n_ > &outobs then stop;
114
+ %end;
115
+ run;
116
+ %put Search query for &table took
117
+ %sysevalf(%sysfunc(datetime())-&check_tm) seconds;
118
+ %if &syscc ne 0 %then %do;
119
+ %put %str(ERR)ROR: SYSCC=&syscc when processing &lib..&table;
120
+ %return;
121
+ %end;
122
+ %if %mf_nobs(&outlib..&table)=0 %then %do;
123
+ proc sql;
124
+ drop table &outlib..&table;
125
+ %end;
126
+ %end;
127
+ %end;
128
+
129
+ %put process finished at %sysfunc(datetime(),datetime19.);
130
+
131
+ %mend mp_searchdata;
@@ -0,0 +1,55 @@
1
+ /**
2
+ @file
3
+ @brief Logs a key value pair a control dataset
4
+ @details If the dataset does not exist, it is created. Usage:
5
+
6
+ %mp_setkeyvalue(someindex,22,type=N)
7
+ %mp_setkeyvalue(somenewindex,somevalue)
8
+
9
+ <h4> SAS Macros </h4>
10
+ @li mf_existds.sas
11
+
12
+ <h4> Related Macros </h4>
13
+ @li mf_getvalue.sas
14
+
15
+ @param [in] key Provide a key on which to perform the lookup
16
+ @param [in] value Provide a value
17
+ @param [in] type= either C or N will populate valc and valn respectively.
18
+ C is default.
19
+ @param [out] libds= define the target table to hold the parameters
20
+
21
+ @version 9.2
22
+ @author Allan Bowe
23
+ @source https://github.com/sasjs/core
24
+
25
+ **/
26
+
27
+ %macro mp_setkeyvalue(key,value,type=C,libds=work.mp_setkeyvalue
28
+ )/*/STORE SOURCE*/;
29
+
30
+ %if not (%mf_existds(&libds)) %then %do;
31
+ data &libds (index=(key/unique));
32
+ length key $64 valc $2048 valn 8 type $1;
33
+ call missing(of _all_);
34
+ stop;
35
+ run;
36
+ %end;
37
+
38
+ proc sql;
39
+ delete from &libds
40
+ where key=symget('key');
41
+ insert into &libds
42
+ set key=symget('key')
43
+ %if &type=C %then %do;
44
+ ,valc=symget('value')
45
+ ,type='C'
46
+ %end;
47
+ %else %do;
48
+ ,valn=symgetn('value')
49
+ ,type='N'
50
+ %end;
51
+ ;
52
+
53
+ quit;
54
+
55
+ %mend mp_setkeyvalue;
@@ -0,0 +1,122 @@
1
+ /**
2
+ @file
3
+ @brief Sorts a SAS dataset in place, preserving constraints
4
+ @details Generally if a dataset contains indexes, then it is not necessary to
5
+ sort it before performing operations such as merges / joins etc.
6
+ That said, there are a few edge cases where it can be desirable:
7
+
8
+ @li To allow adjacent records to be viewed directly in the dataset
9
+ @li To apply compression, or to remove deleted records
10
+ @li To improve performance for specific queries
11
+
12
+ This macro will only work for BASE (V9) engine libraries. It works by
13
+ creating a copy of the dataset (without data, WITH constraints) in the same
14
+ library, appending a sorted view into it, and finally - renaming it.
15
+
16
+ Example usage:
17
+
18
+ proc sql;
19
+ create table work.example as
20
+ select * from sashelp.class;
21
+ alter table work.example
22
+ add constraint pk primary key(name);
23
+ %mp_sortinplace(work.example)
24
+
25
+ @param [in] libds The libref.datasetname that needs to be sorted
26
+
27
+ <h4> SAS Macros </h4>
28
+ @li mf_existds.sas
29
+ @li mf_getengine.sas
30
+ @li mf_getquotedstr.sas
31
+ @li mf_getuniquename.sas
32
+ @li mf_getvarlist.sas
33
+ @li mf_nobs.sas
34
+ @li mp_abort.sas
35
+ @li mp_getpk.sas
36
+
37
+ <h4> Related Macros </h4>
38
+ @li mp_sortinplace.test.sas
39
+
40
+ @version 9.2
41
+ @author Allan Bowe
42
+
43
+ **/
44
+
45
+ %macro mp_sortinplace(libds
46
+ )/*/STORE SOURCE*/;
47
+
48
+ %local lib ds tempds1 tempds2 tempvw sortkey;
49
+
50
+ /* perform validations */
51
+ %mp_abort(iftrue=(%sysfunc(countc(&libds,.)) ne 1)
52
+ ,mac=mp_sortinplace
53
+ ,msg=%str(LIBDS (&libds) should have LIBREF.DATASET format)
54
+ )
55
+ %mp_abort(iftrue=(%mf_existds(&libds)=0)
56
+ ,mac=mp_sortinplace
57
+ ,msg=%str(&libds does not exist)
58
+ )
59
+
60
+ %let lib=%scan(&libds,1,.);
61
+ %let ds=%scan(&libds,2,.);
62
+ %mp_abort(iftrue=(%mf_getengine(&lib) ne V9)
63
+ ,mac=mp_sortinplace
64
+ ,msg=%str(&lib is not a BASE engine library)
65
+ )
66
+
67
+ /* grab a copy of the constraints so we know what to sort by */
68
+ %let tempds1=%mf_getuniquename(prefix=&sysmacroname);
69
+ %mp_getpk(lib=&lib,ds=&ds,outds=work.&tempds1)
70
+
71
+ %if %mf_nobs(work.&tempds1)=0 %then %do;
72
+ %put &sysmacroname: No PK found in &lib..&ds;
73
+ %put Sorting will not take place;
74
+ %return;
75
+ %end;
76
+
77
+ /* fallback sortkey is all fields */
78
+ %let sortkey=%mf_getvarlist(&libds);
79
+
80
+ /* overlay actual sort key if it exists */
81
+ data _null_;
82
+ set work.&tempds1;
83
+ call symputx('sortkey',coalescec(pk_fields,symget('sortkey')));
84
+ run;
85
+
86
+
87
+ /* create empty copy, with ALL constraints, in the same library */
88
+ %let tempds2=%mf_getuniquename(prefix=&sysmacroname);
89
+ proc append base=&lib..&tempds2 data=&libds(obs=0);
90
+ run;
91
+
92
+ /* create sorted view */
93
+ %let tempvw=%mf_getuniquename(prefix=&sysmacroname);
94
+ proc sql;
95
+ create view work.&tempvw as select * from &lib..&ds
96
+ order by %mf_getquotedstr(&sortkey,quote=N);
97
+
98
+ /* append sorted data */
99
+ proc append base=&lib..&tempds2 data=work.&tempvw;
100
+ run;
101
+
102
+ /* do validations */
103
+ %mp_abort(iftrue=(&syscc ne 0)
104
+ ,mac=mp_sortinplace
105
+ ,msg=%str(syscc=&syscc prior to replace operation)
106
+ )
107
+ %mp_abort(iftrue=(%mf_nobs(&lib..&tempds2) ne %mf_nobs(&lib..&ds))
108
+ ,mac=mp_sortinplace
109
+ ,msg=%str(new dataset has a different number of logical obs to the old)
110
+ )
111
+
112
+ /* drop old dataset */
113
+ proc sql;
114
+ drop table &lib..&ds;
115
+
116
+ /* rename the new dataset */
117
+ proc datasets library=&lib;
118
+ change &tempds2=&ds;
119
+ run;
120
+
121
+
122
+ %mend mp_sortinplace;