Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mp_filterstore.sas
Go to the documentation of this file.
1/**
2 @file
3 @brief Checks & Stores an input filter table and returns the Filter Key
4 @details Used to generate a FILTER_RK from an input query dataset. This
5 process requires several permanent tables (names are configurable). The
6 benefit of storing query values at backend is to enable stored 'views' of
7 filtered tables at frontend (ie, when building [SAS-Powered Apps](
8 https://sasapps.io)). This macro is also used in [Data Controller for SAS](
9 https://datacontroller.io).
10
11 A more recent feature of this macro is the ability to support filter queries
12 on Format Catalogs. This is achieved by adding a `-FC` suffix to the `libds`
13 parameter - where the "ds" in this case is the catalog name.
14
15
16 @param [in] libds= The target dataset to be filtered (lib should be assigned).
17 If filtering a format catalog, add the following suffix: `-FC`.
18 @param [in] queryds= (WORK.FILTERQUERY) The temporary input query dataset to
19 be validated. Has the following format:
20|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
21|---|---|---|---|---|---|
22|AND|AND|1|SOME_BESTNUM|>|1|
23|AND|AND|1|SOME_TIME|=|77333|
24 @param [in] filter_summary= (PERM.FILTER_SUMMARY) Permanent table containing
25 summary filter values. The definition is available by running
26 mp_coretable.sas as follows: `mp_coretable(FILTER_SUMMARY)`. Example
27 values:
28|FILTER_RK:best.|FILTER_HASH:$32.|FILTER_TABLE:$41.|PROCESSED_DTTM:datetime19.|
29|---|---|---|---|
30|`1 `|`540E96F566D194AB58DD4C413C99C9DB `|`VIYA6014.MPE_TABLES `|`1956084246 `|
31|`2 `|`87737DB9EEE2650F5C89956CEAD0A14F `|`VIYA6014.MPE_X_TEST `|`1956084452.1`|
32|`3 `|`8048BD908DBBD83D013560734E90D394 `|`VIYA6014.MPE_TABLES `|`1956093620.6`|
33 @param [in] filter_detail= (PERM.FILTER_DETAIL) Permanent table containing
34 detailed (raw) filter values. The definition is available by running
35 mp_coretable.sas as follows: `mp_coretable(FILTER_DETAIL)`. Example
36 values:
37|FILTER_HASH:$32.|FILTER_LINE:best.|GROUP_LOGIC:$3.|SUBGROUP_LOGIC:$3.|SUBGROUP_ID:best.|VARIABLE_NM:$32.|OPERATOR_NM:$12.|RAW_VALUE:$4000.|PROCESSED_DTTM:datetime19.|
38|---|---|---|---|---|---|---|---|---|
39|`540E96F566D194AB58DD4C413C99C9DB `|`1 `|`AND `|`AND `|`1 `|`LIBREF `|`CONTAINS `|`DC`|`1956084245.8 `|
40|`540E96F566D194AB58DD4C413C99C9DB `|`2 `|`AND `|`OR `|`2 `|`DSN `|`= `|` MPE_LOCK_ANYTABLE `|`1956084245.8 `|
41|`87737DB9EEE2650F5C89956CEAD0A14F `|`1 `|`AND `|`AND `|`1 `|`PRIMARY_KEY_FIELD `|`IN `|`(1,2,3) `|`1956084451.9 `|
42 @param [in] lock_table= (PERM.LOCK_TABLE) Permanent locking table. Used to
43 manage concurrent access. The definition is available by running
44 mp_coretable.sas as follows: `mp_coretable(LOCKTABLE)`.
45 @param [in] maxkeytable= (0) Optional permanent reference table used for
46 retained key tracking. Described in mp_retainedkey.sas.
47 @param [in] mdebug= (1) set to 1 to enable DEBUG messages
48 @param [out] outresult= (work.result) The result table with the FILTER_RK
49 @param [out] outquery= (work.query) The original query, taken as extract
50 after table load
51
52
53 <h4> SAS Macros </h4>
54 @li mddl_sas_cntlout.sas
55 @li mf_getuniquename.sas
56 @li mf_getvalue.sas
57 @li mf_islibds.sas
58 @li mf_nobs.sas
59 @li mp_abort.sas
60 @li mp_filtercheck.sas
61 @li mp_hashdataset.sas
62 @li mp_retainedkey.sas
63
64 <h4> Related Macros </h4>
65 @li mp_filtercheck.sas
66 @li mp_filtergenerate.sas
67 @li mp_filtervalidate.sas
68 @li mp_filterstore.test.sas
69
70 @version 9.2
71 @author [Allan Bowe](https://www.linkedin.com/in/allanbowe)
72
73**/
74
75%macro mp_filterstore(libds=,
76 queryds=work.filterquery,
77 filter_summary=PERM.FILTER_SUMMARY,
78 filter_detail=PERM.FILTER_DETAIL,
79 lock_table=PERM.LOCK_TABLE,
80 maxkeytable=PERM.MAXKEYTABLE,
81 outresult=work.result,
82 outquery=work.query,
83 mdebug=1
84);
85%put &sysmacroname entry vars:;
86%put _local_;
87
88%local ds0 ds1 ds2 ds3 ds4 filter_hash orig_libds;
89%let libds=%upcase(&libds);
90%let orig_libds=&libds;
91
92%mp_abort(iftrue= (&syscc ne 0)
93 ,mac=mp_filterstore
94 ,msg=%str(syscc=&syscc on macro entry)
95)
96%mp_abort(iftrue= (%mf_islibds(&filter_summary)=0)
97 ,mac=mp_filterstore
98 ,msg=%str(Invalid filter_summary value: &filter_summary)
99)
100%mp_abort(iftrue= (%mf_islibds(&filter_detail)=0)
101 ,mac=mp_filterstore
102 ,msg=%str(Invalid filter_detail value: &filter_detail)
103)
104%mp_abort(iftrue= (%mf_islibds(&lock_table)=0)
105 ,mac=mp_filterstore
106 ,msg=%str(Invalid lock_table value: &lock_table)
107)
108
109/**
110 * validate query
111 * use format catalog export, if a format
112 */
113%if "%substr(&libds,%length(&libds)-2,3)"="-FC" %then %do;
114 %let libds=%scan(&libds,1,-); /* chop off -FC extension */
115 %let ds0=%mf_getuniquename(prefix=fmtds_);
116 %let libds=&ds0;
117 /*
118 There is no need to export the entire format catalog here - the validations
119 are done against the data model, not the data values. So we can simply
120 hardcode the structure based on the cntlout dataset.
121 */
122 %mddl_sas_cntlout(libds=&ds0)
123
124%end;
125%mp_filtercheck(&queryds,targetds=&libds,abort=YES)
126
127/* hash the result */
128%let ds1=%mf_getuniquename(prefix=hashds);
129%mp_hashdataset(&queryds,outds=&ds1,salt=&orig_libds)
130%let filter_hash=%upcase(%mf_getvalue(&ds1,hashkey));
131%if &mdebug=1 %then %do;
132 data _null_;
133 putlog "filter_hash=&filter_hash";
134 set &ds1;
135 putlog (_all_)(=);
136 run;
137%end;
138
139/* check if data already exists for this hash */
140data &outresult;
141 set &filter_summary;
142 where filter_hash="&filter_hash";
143run;
144
145%mp_abort(iftrue= (&syscc ne 0)
146 ,mac=mp_filterstore
147 ,msg=%str(syscc=&syscc after hash check)
148)
149%mp_abort(iftrue= ("&filter_hash "=" ")
150 ,mac=mp_filterstore
151 ,msg=%str(problem with filter_hash generation)
152)
153
154%if %mf_nobs(&outresult)=0 %then %do;
155
156 /* first update summary table */
157 %let ds3=%mf_getuniquename(prefix=filtersum);
158 data work.&ds3;
159 if 0 then set &filter_summary;
160 filter_table="&orig_libds";
161 filter_hash="&filter_hash";
162 PROCESSED_DTTM=%sysfunc(datetime());
163 output;
164 stop;
165 run;
166
167 %mp_lockanytable(LOCK,
168 lib=%scan(&filter_summary,1,.)
169 ,ds=%scan(&filter_summary,2,.)
170 ,ref=MP_FILTERSTORE summary update - &filter_hash
171 ,ctl_ds=&lock_table
172 )
173
174 %let ds4=%mf_getuniquename(prefix=filtersumappend);
175 %mp_retainedkey(
176 base_lib=%scan(&filter_summary,1,.)
177 ,base_dsn=%scan(&filter_summary,2,.)
178 ,append_lib=work
179 ,append_dsn=&ds3
180 ,retained_key=filter_rk
181 ,business_key=filter_hash
182 ,maxkeytable=&maxkeytable
183 ,locktable=&lock_table
184 ,outds=work.&ds4
185 )
186 proc append base=&filter_summary data=&ds4;
187 run;
188
189 %mp_lockanytable(UNLOCK,
190 lib=%scan(&filter_summary,1,.)
191 ,ds=%scan(&filter_summary,2,.)
192 ,ref=MP_FILTERSTORE summary update - &filter_hash
193 ,ctl_ds=&lock_table
194 )
195
196 %if &syscc ne 0 %then %do;
197 data _null_;
198 set &ds4;
199 putlog (_all_)(=);
200 run;
201 %goto err;
202 %end;
203
204 data &outresult;
205 set &filter_summary;
206 where filter_hash="&filter_hash";
207 run;
208
209 /* Next, update detail table */
210 %let ds2=%mf_getuniquename(prefix=filterdetail);
211 data &ds2;
212 if 0 then set &filter_detail;
213 set &queryds;
214 format filter_hash $hex32. filter_line 8.;
215 filter_hash="&filter_hash";
216 filter_line=_n_;
217 PROCESSED_DTTM=%sysfunc(datetime());
218 run;
219 %mp_lockanytable(LOCK,
220 lib=%scan(&filter_detail,1,.)
221 ,ds=%scan(&filter_detail,2,.)
222 ,ref=MP_FILTERSTORE update - &filter_hash
223 ,ctl_ds=&lock_table
224 )
225 proc append base=&filter_detail data=&ds2;
226 run;
227
228 %mp_lockanytable(UNLOCK,
229 lib=%scan(&filter_detail,1,.)
230 ,ds=%scan(&filter_detail,2,.)
231 ,ref=MP_FILTERSTORE detail update &filter_hash
232 ,ctl_ds=&lock_table
233 )
234
235 %if &syscc ne 0 %then %do;
236 data _null_;
237 set &ds2;
238 putlog (_all_)(=);
239 run;
240 %goto err;
241 %end;
242
243%end;
244
245proc sort data=&filter_detail(where=(filter_hash="&filter_hash")) out=&outquery;
246 by filter_line;
247run;
248
249%err:
250%mp_abort(iftrue= (&syscc ne 0)
251 ,mac=mp_filterstore
252 ,msg=%str(syscc=&syscc on macro exit)
253)
254
255%mend mp_filterstore;