SASUnit Examples  Version 1.5.0
assertforeignkey.sas
Go to the documentation of this file.
1 
39 %MACRO assertForeignKey (i_mstrLib =
40  ,i_mstMem =
41  ,i_mstKey =
42  ,i_unique = TRUE
43  ,i_lookupLib =
44  ,i_lookupMem =
45  ,i_lookupKey =
46  ,i_cmpKeyLen = 1
47  ,o_maxObsRprtFail = MAX
48  ,o_listingVars =
49  ,o_treatMissings = VALUE
50  ,i_desc = Check for foreign key relation
51  );
52 
53  %GLOBAL g_inTestcase;
54  %LOCAL l_dsMstrName l_dsLookupName l_MstrVars l_LookupVars l_renameLookup l_actual l_helper l_helper1 l_vartypMstr
55  l_vartypLookup l_rc l_result l_cnt1 l_cnt2 l_casid l_tstid l_path i l_listingVars num_missing l_treatMissings
56  l_treatMissing l_unique l_errMsg;
57 
58  %LET l_actual = -999;
59  %LET l_dsMstrName = &i_mstrLib..&i_mstMem.;
60  %LET l_dsLookupName = &i_lookupLib..&i_lookupMem.;
61  %LET i_mstKey = %SYSFUNC(compbl(&i_mstKey.));
62  %LET i_lookupKey = %SYSFUNC(compbl(&i_lookupKey.));
63  %LET l_listingVars = %SYSFUNC(COMPBL(&o_listingVars. %str( )));
64  %LET l_treatMissings = %SYSFUNC(upcase(&o_treatMissings.));
65  %LET l_unique = %SYSFUNC(upcase(&i_unique.));
66  %LET l_result = 2;
67  %LET l_errMsg =;
68 
69  /*-- verify correct sequence of calls-----------------------------------------*/
70  %IF &g_inTestcase EQ 1 %THEN %DO;
71  %endTestcall;
72  %END;
73  %ELSE %IF &g_inTestcase NE 2 %THEN %DO;
74  %PUT &g_error.(SASUNIT): assert must be called after initTestcase;
75  %RETURN;
76  %END;
77 
78  %*************************************************************;
79  %*** Check preconditions ***;
80  %*************************************************************;
81 
82  %*** check for valid librefs und existence of data sets Master und Lookup***;
83  %IF ((%SYSFUNC (libref (&i_mstrLib.)) NE 0) or (%SYSFUNC(exist(&l_dsMstrName)) EQ 0)) %THEN %DO;
84  %LET l_actual =-1;
85  %LET l_errMsg =Libref of master table not valid or data set does not exist;
86  %GOTO Update;
87  %END;
88  %IF ((%SYSFUNC (libref (&i_lookupLib.)) NE 0) or (%SYSFUNC(exist(&l_dsLookupName)) EQ 0)) %THEN %DO;
89  %LET l_actual =-2;
90  %LET l_errMsg =Libref of lookup table not valid or data set does not exist;
91  %GOTO Update;
92  %END;
93 
94  %*** Is the number of keys specified in i_cmpKeyLen the same as actually specified in i_mstKey and i_lookupKey***;
95  %LET l_helper = %eval(%SYSFUNC(count(&i_mstKey,%str( )))+1);
96  %IF (&l_helper. NE &i_cmpKeyLen.) %THEN %DO;
97  %LET l_actual =-3;
98  %LET l_errMsg =Number of keys found in i_mstKey not compatible to specified number;
99  %GOTO Update;
100  %END;
101  %LET l_helper = %eval(%SYSFUNC(count(&i_lookupKey,%str( )))+1);
102  %IF (&l_helper. NE &i_cmpKeyLen.) %THEN %DO;
103  %LET l_actual =-4;
104  %LET l_errMsg =Number of found keys in i_lookupKey not compatible to specified number;
105  %GOTO Update;
106  %END;
107 
108  %*** Extract given keys to local variables***;
109  %DO i=1 %TO &i_cmpKeyLen.;
110  %local l_mstKey&i l_lookupKey&i;
111  %LET l_mstKey&i = %SYSFUNC(scan(&i_mstKey, &i., " "));
112  %LET l_lookupKey&i = %SYSFUNC(scan(&i_lookupKey, &i., " "));
113  %END;
114 
115  %*** Check if parameter o_maxObsRprtFail is valid ***;
116  %IF NOT (%SYSFUNC(upcase(&o_maxObsRprtFail.)) = MAX) %THEN %DO;
117  %IF (%datatyp(&o_maxObsRprtFail.) ~=NUMERIC) %THEN %DO;
118  %LET l_actual =-19;
119  %LET l_errMsg =%bquote(Parameter o_maxObsRprtFail (&o_maxObsRprtFail): MAX or numeric GE 0);
120  %GOTO Update;
121  %END;
122  %ELSE %IF (&o_maxObsRprtFail. < 0) %THEN %DO;
123  %LET l_actual =-20;
124  %LET l_errMsg =%bquote(Parameter o_maxObsRprtFail(&o_maxObsRprtFail) < 0);
125  %GOTO Update;
126  %END;
127  %END;
128 
129  %*** Check existence of specified keys in their respective tables***;
130  %*** open specified tables ***;
131  %LET l_dsMstid = %SYSFUNC(open(&l_dsMstrName.));
132  %LET l_dsLookupid = %SYSFUNC(open(&l_dsLookupName.));
133  %*** opened correctly? ***;
134  %IF (&l_dsMstid. EQ 0 or &l_dsLookupid. EQ 0) %THEN %DO;
135  %LET l_actual = -9;
136  %LET l_errMsg =Open function failed;
137  %GOTO Update;
138  %END;
139 
140  %*** loop through all variables ***;
141  %DO i=1 %TO &i_cmpKeyLen.;
142  %LET l_helper = %SYSFUNC(varnum(&l_dsMstid., &&l_mstKey&i.));
143  %IF &l_helper. = 0 %THEN %DO;
144  %* specified variable not found;
145  %LET l_actual = -5;
146  %LET l_errMsg =Key in master table not found;
147  %GOTO Update;
148  %END;
149  %ELSE %DO;
150  %* specified variable found: get variable type;
151  %LET l_vartypMstr = %SYSFUNC(vartype(&l_dsMstid., &l_helper.));
152 
153  %*** Concatenate String for sql where condition: find missing values ***;
154  %IF &l_vartypMstr. =N %THEN %DO;
155  %LET l_helper1 = %str(.);
156  %END;
157  %ELSE %DO;
158  %LET l_helper1 = %str("");
159  %END;
160  %*** Insert or into sql where condition if loop runs more than once ***;
161  %IF &i > 1 %THEN %DO;
162  %LET l_treatMissing = &l_treatMissing. OR;
163  %END;
164  %LET l_treatMissing = &l_treatMissing. &&l_mstKey&i. %str(=) &l_helper1;
165  %END;
166 
167  %LET l_helper = %SYSFUNC(varnum(&l_dsLookupid.,&&l_lookupKey&i.));
168  %IF (&l_helper. EQ 0) %THEN %DO;
169  %* specified variable not found;
170  %LET l_actual = -6;
171  %LET l_errMsg = Key in lookup table not found;
172  %GOTO Update;
173  %END;
174  %ELSE %DO;
175  %* specified variable found: get variable type;
176  %LET l_vartypLookup = %SYSFUNC(vartype(&l_dsLookupid., &l_helper.));
177  %END;
178 
179  %* Same Data Type?;
180  %IF (&l_vartypMstr. NE &l_vartypLookup.) %THEN %DO;
181  %* specified variable not found;
182  %LET l_actual = -7;
183  %LET l_errMsg =Variable types of keys in master and lookup table do not match;
184  %GOTO Update;
185  %END;
186  %END;
187 
188  %*** loop through l_listingVars: Check if valid ***;
189  %LET i = 1;
190  %LET l_helper1 = %SYSFUNC(scan(&l_listingVars., &i., %str( )));
191 
192  %DO %UNTIL (&l_helper1=%str( ));
193  %IF (&l_helper1. =) %THEN %DO;
194  %GOTO Continue;
195  %END;
196  %LET l_helper = %SYSFUNC(varnum(&l_dsMstid., &l_helper1.));
197  %IF &l_helper. = 0 %THEN %DO;
198  %* specified variable not found;
199  %LET l_actual = -21;
200  %LET l_errMsg =%bquote(Parameter o_listingVars (&l_listingVars) not found in Master Table);
201  %GOTO Update;
202  %END;
203  %LET i = %eval(&i+1);
204  %LET l_helper1 = %SYSFUNC(scan(&l_listingVars., &i., %str( )));
205  %END;
206  %Continue:
207  %LET l_listingVars= &i_mstKey. &l_listingVars.;
208  %LET l_rc=%SYSFUNC(close(&l_dsMstid.));
209  %LET l_rc=%SYSFUNC(close(&l_dsLookupid.));
210 
211  %*** parameter l_treatMissings: handle different cases ***;
212  %*** make local copy of master table*;
213  data mstrCopy;
214  set &l_dsMstrName.;
215  run;
216 
217  %*** check for valid parameters*;
218  %IF (&l_treatMissings. NE IGNORE AND &l_treatMissings. NE DISALLOW AND &l_treatMissings. NE VALUE) %THEN %DO;
219  %LET l_actual = -22;
220  %LET l_errMsg = %bquote(Invalid argument für parameter treatMissings (&l_treatMissings));
221  %GOTO Update;
222  %END;
223 
224  %*** get number of missing keys in master table*;
225  PROC SQL;
226  create table master_missing as
227  select *
228  from mstrCopy
229  where &l_treatMissing.;
230  ;
231  QUIT;
232 
233  %***get number of observations ***;
234  %LET l_helper =%SYSFUNC(open(master_missing));
235  %LET num_missing =%SYSFUNC(attrn(&l_helper,nlobs));
236  %LET l_rc =%SYSFUNC(close(&l_helper));
237 
238  %*** Exit if missings were found***;
239  %IF ("&l_treatMissings." = "DISALLOW" AND &num_missing. GT 0) %THEN %DO;
240  %LET l_actual = -23;
241  %LET l_errMsg = %str(Parameter treatMissingsMst set to disallow, but missings found in master table);
242  %GOTO Update;
243  %END;
244  %ELSE %IF ("&l_treatMissings." EQ "IGNORE") %THEN %DO;
245  %*** delete missing values ***;
246  PROC SQL;
247  delete from mstrCopy
248  where &l_treatMissing.;
249  ;
250  QUIT;
251  %END;
252 
253  %*** check for valid parameter i_unique ***;
254  %IF (&l_unique. NE TRUE AND &l_unique. NE FALSE) %THEN %DO;
255  %LET l_actual = -24;
256  %LET l_errMsg =Value for parameter i_unique not valid (&l_unique);
257  %GOTO Update;
258  %END;
259 
260  %*************************************************************;
261  %*** start tests ***;
262  %*************************************************************;
263 
264  %*** Get distinct values from lookup table***;
265  %DO i=1 %to &i_cmpKeyLen.;
266  %IF (&i>1) %THEN %DO;
267  %*** Insert comma into sql select clause ***;
268  %LET l_LookupVars = &l_LookupVars. ,;
269  %END;
270  %LET l_MstrVars = &l_MstrVars. &&l_mstKey&i.;
271  %LET l_LookupVars = &l_LookupVars. &&l_lookupKey&i.;
272  %LET l_renameLookup = &l_renameLookup. &&l_lookupKey&i.=&&l_mstKey&i.;
273  %END;
274 
275  %*** Check whether specified key is unique for lookup table ***;
276  PROC SQL noprint;
277  create table distKeysLookUp as
278  SELECT distinct &l_LookupVars.
279  FROM &l_dsLookupName.
280  ;
281  QUIT;
282 
283  %*** Count nobs from specified lookup table: May contain duplicate key values***;
284  PROC SQL noprint;
285  SELECT count(*) into :l_cnt1
286  FROM &l_dsLookupName.
287  ;
288  QUIT;
289 
290  %*** Count nobs in distKeysLookUp: Contains only distinct keys***;
291  PROC SQL noprint;
292  SELECT count(*) into: l_cnt2
293  FROM work.distKeysLookUp
294  ;
295  QUIT;
296 
297  %*** Is parameter l_unique set to true -> are duplicates allowed? ***;
298  %IF (("&l_unique." EQ "TRUE") AND (&l_cnt1. NE &l_cnt2.)) %THEN %DO;
299  %LET l_actual = -8;
300  %LET l_errMsg =%str(Specified key of lookup table not unique, check parameter i_unique or lookup table);
301  %GOTO Update;
302  %END;
303  %*** if parameter l_unique is set to false, put warning to log, but go on processing ***;
304  %ELSE %IF (("&l_unique." EQ "FALSE") AND (&l_cnt1. NE &l_cnt2.))%THEN %DO;
305  %PUT g_warning.(SASUNIT): Parameter i_unique set to false and lookup table not unique;
306  %END;
307 
308  %*** Check whether all keys in the master table are available in the lookup table***;
309  proc sort data = mstrCopy out = mstrSorted;
310  by &l_MstrVars;
311  run;
312  data keyNotFndMstr keyNotFndLookUp;
313  merge mstrSorted(in=fndMstr) distKeysLookUp(in=fndLookUp rename=(&l_renameLookup.));
314  by &l_MstrVars.;
315  if fndLookUp AND not fndMstr then output keyNotFndMstr;
316  If not fndLookUp AND fndMstr then output keyNotFndLookUp;
317  run;
318 
319  %*** Who many keys from the master table were not found in the lookup table ***;
320  %LET l_helper =%SYSFUNC(OPEN(work.keyNotFndLookUp,IN));
321  %LET l_actual =%SYSFUNC(ATTRN(&l_helper,NOBS));
322  %LET l_rc =%SYSFUNC(CLOSE(&l_helper));
323 
324  %*** Test successful? l_actual < 0 -> error_message, l_actual > 0 -> no foreign key relationship***;
325  %IF (&l_actual. = 0) %THEN %DO;
326  %LET l_result = 0;
327  %END;
328 
329  %IF (&l_actual. > 0) %THEN %DO;
330  %LET l_errMsg = &l_actual. key(s) not found in lookup table;
331  %END;
332 
333  /*-- get current ids for test case and test ---------------------------------*/
334  %_getScenarioTestId (i_scnid=&g_scnid, r_casid=l_casid, r_tstid=l_tstid);
335 
336  %*** create subfolder ***;
337  %_createTestSubfolder (i_assertType =assertForeignKey
338  ,i_scnid =&g_scnid.
339  ,i_casid =&l_casid.
340  ,i_tstid =&l_tstid.
341  ,r_path =l_path
342  );
343 
344  /* copy data sets if they exist */
345  %LET l_helper= %SYSFUNC(getoption(work));
346  libname tar_afk "&l_path.";
347  %IF %SYSFUNC(fileexist(&l_helper./keyNotFndLookUp.sas7bdat)) NE 0 %THEN %DO;
348  /*Subset data set, keep only key variables + variables specified in l_listingVars*/
349  data keyNotFndLookUp;
350  set keyNotFndLookUp(OBS=&o_maxObsRprtFail.);
351  keep &l_listingVars;
352  run;
353 
354  proc copy in = work out = tar_afk;
355  select keyNotFndLookUp;
356  run;
357  %END;
358  %IF %SYSFUNC(fileexist(&l_helper./keyNotFndMstr.sas7bdat)) NE 0 %THEN %DO;
359  data keyNotFndMstr;
360  set keyNotFndMstr(OBS=&o_maxObsRprtFail.);
361  keep &l_listingVars;
362  run;
363 
364  proc copy in = work out = tar_afk;
365  select keyNotFndMstr;
366  run;
367  %END;
368 
369  %Update:
370  %_asserts(i_type = assertForeignKey
371  ,i_expected = %str(&l_unique.)
372  ,i_actual = %str(&l_actual)
373  ,i_desc = &i_desc.
374  ,i_result = &l_result.
375  ,i_errMsg = &l_errMsg.
376  )
377 
378 %MEND assertForeignKey;