Give that the data are sorted by ID, there may be a performance advantage in using a data step with a BY ID construct:
data have;
input ID Med $ (Start_date End_date) (:mmddyy10.);
format Start_date End_date yymmdd10.;
datalines;
1 A 05/25/2017 10/30/2017
1 B 05/26/2017 06/03/2017
1 C 10/30/2017 10/30/2019
1 XX 01/02/2020 02/03/2020
2 A 03/04/2015 04/25/2015
2 XY 04/23/2015 04/22/2016
2 YY 04/22/2016 04/21/2017
3 XY 02/22/2019 03/21/2019
3 A 03/18/2019 05/15/2019
3 B 05/16/2019 02/01/2020
3 YY 01/20/2017 06/30/2017
3 YZ 01/01/2020 05/05/2020
;
%let begdate=01jan2015;
%let enddate=31dec2020;
data want (drop=_:);
array med_range {1:2,%sysevalf("&begdate"d):%sysevalf("&enddate"d)} _temporary_ ;
set have (in=first_pass) have (in=final_pass);
by id;
if first.id then call missing(of med_range{*});
if med in ('A','B','C') then _grp=1;
else _grp=2;
if first_pass then do _d=start_date to end_date;
med_range{_grp,_d}=1;
end;
if final_pass;
flag=0;
_xgrp=3-_grp; /*_grp=2 maps to _xgrp=1 and 1 maps to 2*/
do _d=start_date to end_date until (flag=1);
if med_range{_xgrp,_d}=1 then flag=1;
end;
run;
Each by group is processed twice. The first pass sets up a 2-row matrix corresponding to the date range universe of your data. Row 1 (_GRP=1) gets assigned 1's for each date group 1 is encountered, row 2 has the same for group 2.
The second pass compares the date range of the record-in-hand with the corresponding dates in the matrix corresponding to the complimentary drug group (_XGRP) and sets a flag if there is an overlap.
... View more