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