Macros for SAS Application Developers
https://github.com/sasjs/core
mp_recursivejoin.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Returns all children from a hierarchy table for a specified parent
4  @details Where data stores hierarchies in a simple parent / child mapping,
5  it is not always straightforward to extract all the children for a
6  particular parent. This problem is known as a recursive self join. This
7  macro will extract all the descendents for a parent.
8  Usage:
9 
10  data have;
11  p=1;c=2;output;
12  p=2;c=3;output;
13  p=2;c=4;output;
14  p=3;c=5;output;
15  p=6;c=7;output;
16  p=8;c=9;output;
17  run;
18 
19  %mp_recursivejoin(base_ds=have
20  ,outds=want
21  ,matchval=1
22  ,parentvar=p
23  ,childvar=c
24  )
25 
26  @param [in] base_ds= base table containing hierarchy (not modified)
27  @param [out] outds= the output dataset to create with the generated hierarchy
28  @param [in] matchval= the ultimate parent from which to filter
29  @param [in] parentvar= name of the parent variable
30  @param [in] childvar= () name of the child variable (should be same type as
31  parent)
32  @param [in] mdebug= set to 1 to prevent temp tables being dropped
33 
34 
35  @returns outds contains the following variables:
36  - level (0 = top level)
37  - &parentvar
38  - &childvar (null if none found)
39 
40  @version 9.2
41  @author Allan Bowe
42 
43 **/
44 
45 %macro mp_recursivejoin(base_ds=
46  ,outds=
47  ,matchval=
48  ,parentvar=
49  ,childvar=
50  ,iter= /* reserved for internal / recursive use by the macro itself */
51  ,maxiter=500 /* avoid infinite loop */
52  ,mDebug=0);
53 
54 %if &iter= %then %do;
55  proc sql;
56  create table &outds as
57  select 0 as level,&parentvar, &childvar
58  from &base_ds
59  where &parentvar=&matchval;
60  %if &sqlobs.=0 %then %do;
61  %put NOTE: &sysmacroname: No match for &parentvar=&matchval;
62  %return;
63  %end;
64  %let iter=1;
65 %end;
66 %else %if &iter>&maxiter %then %return;
67 
68 proc sql;
69 create table _data_ as
70  select &iter as level
71  ,curr.&childvar as &parentvar
72  ,base_ds.&childvar as &childvar
73  from &outds curr
74  left join &base_ds base_ds
75  on curr.&childvar=base_ds.&parentvar
76  where curr.level=%eval(&iter.-1)
77  & curr.&childvar is not null;
78 %local append_ds; %let append_ds=&syslast;
79 %local obs; %let obs=&sqlobs;
80 insert into &outds select distinct * from &append_ds;
81 %if &mdebug=0 %then drop table &append_ds;;
82 
83 %if &obs %then %do;
84  %mp_recursivejoin(iter=%eval(&iter.+1)
85  ,outds=&outds,parentvar=&parentvar
86  ,childvar=&childvar
87  ,base_ds=&base_ds
88  )
89 %end;
90 
91 %mend mp_recursivejoin;