<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Deriving date crossings / date intersections in SCD2 tables in SAS Programming</title>
    <link>https://communities.sas.com/t5/SAS-Programming/Deriving-date-crossings-date-intersections-in-SCD2-tables/m-p/94834#M19979</link>
    <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Note:&amp;nbsp; I've also posted this to SAS-L (&lt;A href="http://www.listserv.uga.edu/cgi-bin/wa?A2=ind1212b&amp;amp;L=sas-l&amp;amp;F=&amp;amp;S=&amp;amp;X=253AE57F5FB820875B&amp;amp;Y=sas_l_739@yahoo.com.au&amp;amp;P=978/" title="http://www.listserv.uga.edu/cgi-bin/wa?A2=ind1212b&amp;amp;L=sas-l&amp;amp;F=&amp;amp;S=&amp;amp;X=253AE57F5FB820875B&amp;amp;Y=sas_l_739@yahoo.com.au&amp;amp;P=978/"&gt;SAS-L archives -- December 2012, week 2 (#8)&amp;lt;/title&amp;gt;&amp;lt;style type="text/css"&amp;gt;&amp;lt;!--BODY { font-family: "Comic Sans MS",arial, helvetica, sans-serif; font-size: 14px; color: black; background-color: white; }TD, TH, FONT, .BOD { font-family: "Comic Sans MS", arial, helvetica, sans-serif; font-size: 14px; margin-left: 0px; margin-right: 0px; color: black; }SUP { font-family: courier,monospace; font-size: 12px; text-decoration: none; }KBD {font-family: courier,monospace;}PRE {font-family: courier,monospace;} .SMALL {font-size: 12px} .SMALLER {font-size: 10px} .LARGE {font-size: 16px} .LARGER {font-size: 18px} .FOOT {font-size: 12px; color: gray } .BLOCK { font-size: 12px; color: #555555; text-align: justify; } .FIXED { font-family: courier,monospaced; font-size:12pt }STRONG {font-weight: bold }EM { font-style: italic; }A:LINK { text-decoration: none; color: #0000FF; }A:VISITED { text-decoration: none; color: #0000FF; }A:ACTIVE { text-decoration: none; color: #0000FF; }A:HOVER { text-decoration: none; color: #ad080a; }H1 { font-size: 22px; }H2 { font-size: 20px; }H3 { font-size: 18px; }--&amp;gt;&amp;lt;/style&amp;gt;&amp;lt;!-- This is the top part for sub pages (topsub.html) 12/10/99 16:05 --&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;LISTSERV at the University of Georgia&lt;/A&gt;) but wanted to also post this here for a greater audience.&amp;nbsp; &lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Hi,&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Apologies for the length of this post.&amp;nbsp; However, most of the length is in code examples which help illustrate the issues.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I need to derive "date crossings" or "date intersections" when joining several SCD Type 2 (SCD2) datasets.&amp;nbsp; Furthermore, I need to do this same thing to correct data issues in single dataset sourced from our CRM system (a self-join scenario).&amp;nbsp; (Note:&amp;nbsp; if there is a standard data warehousing term for "date crossings" or "date intersections", please let me know).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;An example may help illustrate this.&amp;nbsp; Note: I often find it useful to draw these date crossings out on a sheet of paper, like a time continum number line:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* "well-formed" SCD2 data, stretching from "infinity" (01JAN1960) to "infinity" (31DEC9999), ;&lt;/P&gt;&lt;P&gt;* with no gaps in the date ranges, and each new fromDate = previous toDate + 1 ;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 14JAN2010&lt;/P&gt;&lt;P&gt;1 A 15JAN2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B 01JUL2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data two;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 15MAR2010&lt;/P&gt;&lt;P&gt;1 C 16MAR2010 11NOV2011&lt;/P&gt;&lt;P&gt;1 D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* this is the desired output.&amp;nbsp; the first record 01JAN1960 - 14JAN2010 is optional ; &lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A . 15JAN2010 15MAR2010&lt;/P&gt;&lt;P&gt;1 A C 16MAR2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B C 01JUL2011 11NOV2011&lt;/P&gt;&lt;P&gt;1 B D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* this approach works to determine the date crossings ;&lt;/P&gt;&lt;P&gt;* as long as the SCD2 data is "well-formed" ;&lt;/P&gt;&lt;P&gt;proc sql _method stimer;&lt;/P&gt;&lt;P&gt;&amp;nbsp; create table want2 as&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1, t1.var1, t2.var2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,max(t1.fromDate, t2.fromDate) as fromDate format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,min(t1.toDate,&amp;nbsp;&amp;nbsp; t2.toDate)&amp;nbsp;&amp;nbsp; as toDate&amp;nbsp;&amp;nbsp; format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; one t1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; left join&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; two t2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; on&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1 = t2.key1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; calculated fromDate &amp;lt;= calculated toDate&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*&amp;nbsp; * uncomment this to remove the 01JAN1960 - 14JAN2010 record ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; coalescec(var1,var2) is not missing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; order by&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key1, calculated fromDate&lt;/P&gt;&lt;P&gt;&amp;nbsp; ;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;This is an example where we can take advantage of SQL's cartesian product.&amp;nbsp; With the correct use of functions (min, max) and an appropriate where clause, we get the desired results.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;However, note the use of the max function to determine the (smaller) fromDate, and the min function to determine the (larger) toDate.&amp;nbsp; This causes needed records to be removed at the date "boundaries" if the date spans do not start and end with the same value within the two tables.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Here is an example.&amp;nbsp; Say we're working with customer records, and our source system does not initialize the new customer at "infinity" (01JAN1960).&amp;nbsp; Furthermore, say our source system merely closes out the record when a customer leaves the company.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* data without the "infinity" records ;&lt;/P&gt;&lt;P&gt;* i.e. without the initialization record, ;&lt;/P&gt;&lt;P&gt;* and the ending record closed out early ;&lt;/P&gt;&lt;P&gt;* this customer left the company on 15DEC2012 ;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A 15JAN2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B 01JUL2011 15DEC2012&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* this customer changed in some way (say address) on 12NOV2011 but is otherwise active ; &lt;/P&gt;&lt;P&gt;data two;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 C 16MAR2010 11NOV2011&lt;/P&gt;&lt;P&gt;1 D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sql _method stimer;&lt;/P&gt;&lt;P&gt;&amp;nbsp; create table want3 as&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1, t1.var1, t2.var2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,max(t1.fromDate, t2.fromDate) as fromDate format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,min(t1.toDate,&amp;nbsp;&amp;nbsp; t2.toDate)&amp;nbsp;&amp;nbsp; as toDate&amp;nbsp;&amp;nbsp; format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; one t1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; left join&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; two t2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; on&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1 = t2.key1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; calculated fromDate &amp;lt;= calculated toDate&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; coalescec(var1,var2) is not missing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; order by&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key1, calculated fromDate&lt;/P&gt;&lt;P&gt;&amp;nbsp; ;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;This does NOT give us the desired results.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Luckily, these simple data issues are relatively easy to fix.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* ensure all time periods stretch from "infinitity" to "infinity" ;&lt;/P&gt;&lt;P&gt;* Macro to fill in date range "gaps", including boundary conditions ;&lt;/P&gt;&lt;P&gt;%macro FillDates(in=, out=, key=, key2=);&lt;/P&gt;&lt;P&gt;&amp;nbsp; proc sort data=&amp;amp;in out=&amp;amp;in._sorted;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; by &amp;amp;key fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp; data fillDates;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; format &amp;amp;key fromDate toDate lag_toDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; set &amp;amp;in._sorted;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; by &amp;amp;key fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; temp_fromDate=fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; temp_toDate&amp;nbsp; =toDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; lag_toDate&amp;nbsp;&amp;nbsp; =lag(toDate);&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* do not change the order of these steps ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* we have to first output the "main" record, ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* then the "gap" records with missing dimension data ;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if first.&amp;amp;key then lag_toDate=.;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* fill in date range gaps ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; call missing(of &amp;amp;key2);&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (not first.&amp;amp;key) then if (temp_fromDate ne lag_toDate+1) then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fromDate=lag_toDate+1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; toDate=temp_fromDate-1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* create "negative infinity" date boundary record ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (first.&amp;amp;key) then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if temp_fromDate gt "01JAN1960"d then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fromDate="01JAN1960"d;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; toDate=temp_fromDate-1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* create "positive infinity" date boundary record ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (last.&amp;amp;key) then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if temp_toDate lt "31DEC9999"d then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fromDate=temp_toDate+1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; toDate="31DEC9999"d;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; format lag_: temp_: date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; drop lag_: temp_:;&lt;/P&gt;&lt;P&gt;&amp;nbsp; run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp; * now create the final dataset in sorted order ;&lt;/P&gt;&lt;P&gt;&amp;nbsp; proc sort data=fillDates out=&amp;amp;out;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; by &amp;amp;key fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; run;&lt;/P&gt;&lt;P&gt;%mend;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;options mprint;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;%FillDates(in=one, out=one_filled, key=key1, key2=var1)&lt;/P&gt;&lt;P&gt;%FillDates(in=two, out=two_filled, key=key1, key2=var2)&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sql _method stimer;&lt;/P&gt;&lt;P&gt;&amp;nbsp; create table want4 as&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1, t1.var1, t2.var2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,max(t1.fromDate, t2.fromDate) as fromDate format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,min(t1.toDate,&amp;nbsp;&amp;nbsp; t2.toDate)&amp;nbsp;&amp;nbsp; as toDate&amp;nbsp;&amp;nbsp; format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; one_filled t1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; left join&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; two_filled t2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; on&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1 = t2.key1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; calculated fromDate &amp;lt;= calculated toDate&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; coalescec(var1,var2) is not missing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; order by&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key1, calculated fromDate&lt;/P&gt;&lt;P&gt;&amp;nbsp; ;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;This again gives me the desired results.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;So far, so good...&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;HOWEVER, I'm dealing with a few data quality issues, for which I can't come up with a solution.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;#1) Source system does not increment the fromDate/toDate correctly:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 14JAN2010&lt;/P&gt;&lt;P&gt;1 A 14JAN2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B 30JUN2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data two;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 15MAR2010&lt;/P&gt;&lt;P&gt;1 C 15MAR2010 11NOV2011&lt;/P&gt;&lt;P&gt;1 D 11NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A . 15JAN2010 15MAR2010&lt;/P&gt;&lt;P&gt;1 A C 16MAR2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B C 01JUL2011 11NOV2011&lt;/P&gt;&lt;P&gt;1 B D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I think this issue can be addressed by augmenting / modifying the functionality of %FillDates above.&amp;nbsp; Do you agree?&amp;nbsp; Or can you think of a better approach?&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;#2) Source system does not correctly close out old records / create new records when there is a change ;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* note the "A" record goes to infinity, instead of properly closing out an SCD2 data change ;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A 14JAN2010 31DEC9999&lt;/P&gt;&lt;P&gt;1 B 30JUN2011 11NOV2011&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 13JAN2010&amp;nbsp; &lt;/P&gt;&lt;P&gt;1 A 14JAN2010 29JUN2011&lt;/P&gt;&lt;P&gt;1 B 30JUN2011 11NOV2011&lt;/P&gt;&lt;P&gt;1 A 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;In this scenario, the issue is within the single dataset itself (a self-join scenario).&amp;nbsp; In this situation, our source system does not properly close out the "B" record, and instead just "reactivates" the "A" record.&amp;nbsp; This of course causes problems when you need a snapshot of the customer situation at any point in time, plus additional issues when this table is joined with other SCD 2 datasets.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Keep in mind these are simplified examples; the "real" problem might not be with contiguous records.&amp;nbsp; A quick example:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A 14JAN2010 15FEB2011&lt;/P&gt;&lt;P&gt;1 B 15FEB2011 24SEP2011&lt;/P&gt;&lt;P&gt;1 C 24SEP2011 31DEC9999&lt;/P&gt;&lt;P&gt;1 D 05MAR2012 31AUG2012&lt;/P&gt;&lt;P&gt;1 E 31AUG2012 10DEC2012&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 13JAN2010&amp;nbsp; &lt;/P&gt;&lt;P&gt;1 A 14JAN2010 15FEB2011&lt;/P&gt;&lt;P&gt;1 B 16FEB2011 24SEP2011&lt;/P&gt;&lt;P&gt;1 C 25SEP2011 04MAR2012&lt;/P&gt;&lt;P&gt;1 D 05MAR2012 31AUG2012&lt;/P&gt;&lt;P&gt;1 E 01SEP2012 10DEC2012&lt;/P&gt;&lt;P&gt;1 C 11DEC2012 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;In this made up example, the customer moved 6 times, and moved back to a previous address "C" twice (their parent's house, whatever), where he/she currently resides.&amp;nbsp; (Plus it's not incrementing the from/to dates properly).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I'm not sure how to tackle this issue?&amp;nbsp; Perhaps sort on toDate, then something like:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;if toDate = "31DEC9999" then if not last.key1 then do; bad_data_flag=1; further processing??? ; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Any thoughts?&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Thanks,&lt;/P&gt;&lt;P&gt;Scott&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
    <pubDate>Mon, 10 Dec 2012 03:40:16 GMT</pubDate>
    <dc:creator>ScottBass</dc:creator>
    <dc:date>2012-12-10T03:40:16Z</dc:date>
    <item>
      <title>Deriving date crossings / date intersections in SCD2 tables</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Deriving-date-crossings-date-intersections-in-SCD2-tables/m-p/94834#M19979</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Note:&amp;nbsp; I've also posted this to SAS-L (&lt;A href="http://www.listserv.uga.edu/cgi-bin/wa?A2=ind1212b&amp;amp;L=sas-l&amp;amp;F=&amp;amp;S=&amp;amp;X=253AE57F5FB820875B&amp;amp;Y=sas_l_739@yahoo.com.au&amp;amp;P=978/" title="http://www.listserv.uga.edu/cgi-bin/wa?A2=ind1212b&amp;amp;L=sas-l&amp;amp;F=&amp;amp;S=&amp;amp;X=253AE57F5FB820875B&amp;amp;Y=sas_l_739@yahoo.com.au&amp;amp;P=978/"&gt;SAS-L archives -- December 2012, week 2 (#8)&amp;lt;/title&amp;gt;&amp;lt;style type="text/css"&amp;gt;&amp;lt;!--BODY { font-family: "Comic Sans MS",arial, helvetica, sans-serif; font-size: 14px; color: black; background-color: white; }TD, TH, FONT, .BOD { font-family: "Comic Sans MS", arial, helvetica, sans-serif; font-size: 14px; margin-left: 0px; margin-right: 0px; color: black; }SUP { font-family: courier,monospace; font-size: 12px; text-decoration: none; }KBD {font-family: courier,monospace;}PRE {font-family: courier,monospace;} .SMALL {font-size: 12px} .SMALLER {font-size: 10px} .LARGE {font-size: 16px} .LARGER {font-size: 18px} .FOOT {font-size: 12px; color: gray } .BLOCK { font-size: 12px; color: #555555; text-align: justify; } .FIXED { font-family: courier,monospaced; font-size:12pt }STRONG {font-weight: bold }EM { font-style: italic; }A:LINK { text-decoration: none; color: #0000FF; }A:VISITED { text-decoration: none; color: #0000FF; }A:ACTIVE { text-decoration: none; color: #0000FF; }A:HOVER { text-decoration: none; color: #ad080a; }H1 { font-size: 22px; }H2 { font-size: 20px; }H3 { font-size: 18px; }--&amp;gt;&amp;lt;/style&amp;gt;&amp;lt;!-- This is the top part for sub pages (topsub.html) 12/10/99 16:05 --&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;LISTSERV at the University of Georgia&lt;/A&gt;) but wanted to also post this here for a greater audience.&amp;nbsp; &lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Hi,&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Apologies for the length of this post.&amp;nbsp; However, most of the length is in code examples which help illustrate the issues.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I need to derive "date crossings" or "date intersections" when joining several SCD Type 2 (SCD2) datasets.&amp;nbsp; Furthermore, I need to do this same thing to correct data issues in single dataset sourced from our CRM system (a self-join scenario).&amp;nbsp; (Note:&amp;nbsp; if there is a standard data warehousing term for "date crossings" or "date intersections", please let me know).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;An example may help illustrate this.&amp;nbsp; Note: I often find it useful to draw these date crossings out on a sheet of paper, like a time continum number line:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* "well-formed" SCD2 data, stretching from "infinity" (01JAN1960) to "infinity" (31DEC9999), ;&lt;/P&gt;&lt;P&gt;* with no gaps in the date ranges, and each new fromDate = previous toDate + 1 ;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 14JAN2010&lt;/P&gt;&lt;P&gt;1 A 15JAN2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B 01JUL2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data two;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 15MAR2010&lt;/P&gt;&lt;P&gt;1 C 16MAR2010 11NOV2011&lt;/P&gt;&lt;P&gt;1 D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* this is the desired output.&amp;nbsp; the first record 01JAN1960 - 14JAN2010 is optional ; &lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A . 15JAN2010 15MAR2010&lt;/P&gt;&lt;P&gt;1 A C 16MAR2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B C 01JUL2011 11NOV2011&lt;/P&gt;&lt;P&gt;1 B D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* this approach works to determine the date crossings ;&lt;/P&gt;&lt;P&gt;* as long as the SCD2 data is "well-formed" ;&lt;/P&gt;&lt;P&gt;proc sql _method stimer;&lt;/P&gt;&lt;P&gt;&amp;nbsp; create table want2 as&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1, t1.var1, t2.var2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,max(t1.fromDate, t2.fromDate) as fromDate format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,min(t1.toDate,&amp;nbsp;&amp;nbsp; t2.toDate)&amp;nbsp;&amp;nbsp; as toDate&amp;nbsp;&amp;nbsp; format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; one t1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; left join&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; two t2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; on&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1 = t2.key1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; calculated fromDate &amp;lt;= calculated toDate&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*&amp;nbsp; * uncomment this to remove the 01JAN1960 - 14JAN2010 record ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; coalescec(var1,var2) is not missing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; order by&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key1, calculated fromDate&lt;/P&gt;&lt;P&gt;&amp;nbsp; ;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;This is an example where we can take advantage of SQL's cartesian product.&amp;nbsp; With the correct use of functions (min, max) and an appropriate where clause, we get the desired results.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;However, note the use of the max function to determine the (smaller) fromDate, and the min function to determine the (larger) toDate.&amp;nbsp; This causes needed records to be removed at the date "boundaries" if the date spans do not start and end with the same value within the two tables.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Here is an example.&amp;nbsp; Say we're working with customer records, and our source system does not initialize the new customer at "infinity" (01JAN1960).&amp;nbsp; Furthermore, say our source system merely closes out the record when a customer leaves the company.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* data without the "infinity" records ;&lt;/P&gt;&lt;P&gt;* i.e. without the initialization record, ;&lt;/P&gt;&lt;P&gt;* and the ending record closed out early ;&lt;/P&gt;&lt;P&gt;* this customer left the company on 15DEC2012 ;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A 15JAN2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B 01JUL2011 15DEC2012&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* this customer changed in some way (say address) on 12NOV2011 but is otherwise active ; &lt;/P&gt;&lt;P&gt;data two;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 C 16MAR2010 11NOV2011&lt;/P&gt;&lt;P&gt;1 D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sql _method stimer;&lt;/P&gt;&lt;P&gt;&amp;nbsp; create table want3 as&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1, t1.var1, t2.var2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,max(t1.fromDate, t2.fromDate) as fromDate format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,min(t1.toDate,&amp;nbsp;&amp;nbsp; t2.toDate)&amp;nbsp;&amp;nbsp; as toDate&amp;nbsp;&amp;nbsp; format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; one t1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; left join&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; two t2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; on&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1 = t2.key1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; calculated fromDate &amp;lt;= calculated toDate&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; coalescec(var1,var2) is not missing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; order by&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key1, calculated fromDate&lt;/P&gt;&lt;P&gt;&amp;nbsp; ;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;This does NOT give us the desired results.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Luckily, these simple data issues are relatively easy to fix.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* ensure all time periods stretch from "infinitity" to "infinity" ;&lt;/P&gt;&lt;P&gt;* Macro to fill in date range "gaps", including boundary conditions ;&lt;/P&gt;&lt;P&gt;%macro FillDates(in=, out=, key=, key2=);&lt;/P&gt;&lt;P&gt;&amp;nbsp; proc sort data=&amp;amp;in out=&amp;amp;in._sorted;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; by &amp;amp;key fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp; data fillDates;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; format &amp;amp;key fromDate toDate lag_toDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; set &amp;amp;in._sorted;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; by &amp;amp;key fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; temp_fromDate=fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; temp_toDate&amp;nbsp; =toDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; lag_toDate&amp;nbsp;&amp;nbsp; =lag(toDate);&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* do not change the order of these steps ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* we have to first output the "main" record, ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* then the "gap" records with missing dimension data ;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if first.&amp;amp;key then lag_toDate=.;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* fill in date range gaps ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; call missing(of &amp;amp;key2);&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (not first.&amp;amp;key) then if (temp_fromDate ne lag_toDate+1) then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fromDate=lag_toDate+1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; toDate=temp_fromDate-1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* create "negative infinity" date boundary record ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (first.&amp;amp;key) then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if temp_fromDate gt "01JAN1960"d then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fromDate="01JAN1960"d;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; toDate=temp_fromDate-1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; %* create "positive infinity" date boundary record ;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (last.&amp;amp;key) then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if temp_toDate lt "31DEC9999"d then do;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fromDate=temp_toDate+1;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; toDate="31DEC9999"d;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; format lag_: temp_: date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; drop lag_: temp_:;&lt;/P&gt;&lt;P&gt;&amp;nbsp; run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp; * now create the final dataset in sorted order ;&lt;/P&gt;&lt;P&gt;&amp;nbsp; proc sort data=fillDates out=&amp;amp;out;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; by &amp;amp;key fromDate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; run;&lt;/P&gt;&lt;P&gt;%mend;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;options mprint;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;%FillDates(in=one, out=one_filled, key=key1, key2=var1)&lt;/P&gt;&lt;P&gt;%FillDates(in=two, out=two_filled, key=key1, key2=var2)&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;proc sql _method stimer;&lt;/P&gt;&lt;P&gt;&amp;nbsp; create table want4 as&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1, t1.var1, t2.var2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,max(t1.fromDate, t2.fromDate) as fromDate format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ,min(t1.toDate,&amp;nbsp;&amp;nbsp; t2.toDate)&amp;nbsp;&amp;nbsp; as toDate&amp;nbsp;&amp;nbsp; format=date9.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; one_filled t1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; left join&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; two_filled t2&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; on&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; t1.key1 = t2.key1&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; where&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; calculated fromDate &amp;lt;= calculated toDate&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; and&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; coalescec(var1,var2) is not missing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; order by&lt;/P&gt;&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key1, calculated fromDate&lt;/P&gt;&lt;P&gt;&amp;nbsp; ;&lt;/P&gt;&lt;P&gt;quit;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;This again gives me the desired results.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;So far, so good...&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;HOWEVER, I'm dealing with a few data quality issues, for which I can't come up with a solution.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;#1) Source system does not increment the fromDate/toDate correctly:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 14JAN2010&lt;/P&gt;&lt;P&gt;1 A 14JAN2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B 30JUN2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data two;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 15MAR2010&lt;/P&gt;&lt;P&gt;1 C 15MAR2010 11NOV2011&lt;/P&gt;&lt;P&gt;1 D 11NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 var2 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 var2 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A . 15JAN2010 15MAR2010&lt;/P&gt;&lt;P&gt;1 A C 16MAR2010 30JUN2011&lt;/P&gt;&lt;P&gt;1 B C 01JUL2011 11NOV2011&lt;/P&gt;&lt;P&gt;1 B D 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I think this issue can be addressed by augmenting / modifying the functionality of %FillDates above.&amp;nbsp; Do you agree?&amp;nbsp; Or can you think of a better approach?&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;#2) Source system does not correctly close out old records / create new records when there is a change ;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;* note the "A" record goes to infinity, instead of properly closing out an SCD2 data change ;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A 14JAN2010 31DEC9999&lt;/P&gt;&lt;P&gt;1 B 30JUN2011 11NOV2011&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 13JAN2010&amp;nbsp; &lt;/P&gt;&lt;P&gt;1 A 14JAN2010 29JUN2011&lt;/P&gt;&lt;P&gt;1 B 30JUN2011 11NOV2011&lt;/P&gt;&lt;P&gt;1 A 12NOV2011 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;In this scenario, the issue is within the single dataset itself (a self-join scenario).&amp;nbsp; In this situation, our source system does not properly close out the "B" record, and instead just "reactivates" the "A" record.&amp;nbsp; This of course causes problems when you need a snapshot of the customer situation at any point in time, plus additional issues when this table is joined with other SCD 2 datasets.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Keep in mind these are simplified examples; the "real" problem might not be with contiguous records.&amp;nbsp; A quick example:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data one;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 A 14JAN2010 15FEB2011&lt;/P&gt;&lt;P&gt;1 B 15FEB2011 24SEP2011&lt;/P&gt;&lt;P&gt;1 C 24SEP2011 31DEC9999&lt;/P&gt;&lt;P&gt;1 D 05MAR2012 31AUG2012&lt;/P&gt;&lt;P&gt;1 E 31AUG2012 10DEC2012&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;data want;&lt;/P&gt;&lt;P&gt;&amp;nbsp; length key1 var1 $1 fromdate todate 8;&lt;/P&gt;&lt;P&gt;&amp;nbsp; informat fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; format fromdate todate date9.;&lt;/P&gt;&lt;P&gt;&amp;nbsp; input key1 var1 fromdate todate;&lt;/P&gt;&lt;P&gt;&amp;nbsp; datalines;&lt;/P&gt;&lt;P&gt;1 . 01JAN1960 13JAN2010&amp;nbsp; &lt;/P&gt;&lt;P&gt;1 A 14JAN2010 15FEB2011&lt;/P&gt;&lt;P&gt;1 B 16FEB2011 24SEP2011&lt;/P&gt;&lt;P&gt;1 C 25SEP2011 04MAR2012&lt;/P&gt;&lt;P&gt;1 D 05MAR2012 31AUG2012&lt;/P&gt;&lt;P&gt;1 E 01SEP2012 10DEC2012&lt;/P&gt;&lt;P&gt;1 C 11DEC2012 31DEC9999&lt;/P&gt;&lt;P&gt;;&lt;/P&gt;&lt;P&gt;run;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;In this made up example, the customer moved 6 times, and moved back to a previous address "C" twice (their parent's house, whatever), where he/she currently resides.&amp;nbsp; (Plus it's not incrementing the from/to dates properly).&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;I'm not sure how to tackle this issue?&amp;nbsp; Perhaps sort on toDate, then something like:&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;if toDate = "31DEC9999" then if not last.key1 then do; bad_data_flag=1; further processing??? ; end;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Any thoughts?&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Thanks,&lt;/P&gt;&lt;P&gt;Scott&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Mon, 10 Dec 2012 03:40:16 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Deriving-date-crossings-date-intersections-in-SCD2-tables/m-p/94834#M19979</guid>
      <dc:creator>ScottBass</dc:creator>
      <dc:date>2012-12-10T03:40:16Z</dc:date>
    </item>
    <item>
      <title>Re: Deriving date crossings / date intersections in SCD2 tables</title>
      <link>https://communities.sas.com/t5/SAS-Programming/Deriving-date-crossings-date-intersections-in-SCD2-tables/m-p/94835#M19980</link>
      <description>&lt;HTML&gt;&lt;HEAD&gt;&lt;/HEAD&gt;&lt;BODY&gt;&lt;P&gt;Hoooo, It is So long . I think it should be easy. if you split the range into every single date , and transpose it from wide to long. You will get it.&lt;/P&gt;&lt;P&gt;Assuming today is infinite date '31dec9999'd to avoid a large dataset, you can change it back after you get the result.&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;PRE&gt;data one;
&amp;nbsp; length key1 var1 $1 fromdate todate 8;
&amp;nbsp; informat fromdate todate date9.;
&amp;nbsp; format fromdate todate date9.;
&amp;nbsp; input key1 var1 fromdate todate;
&amp;nbsp; datalines;
1 . 01JAN1960 14JAN2010
1 A 15JAN2010 30JUN2011
1 B 01JUL2011 31DEC9999
;
run;

data two;
&amp;nbsp; length key1 var2 $1 fromdate todate 8;
&amp;nbsp; informat fromdate todate date9.;
&amp;nbsp; format fromdate todate date9.;
&amp;nbsp; input key1 var2 fromdate todate;
&amp;nbsp; datalines;
1 . 01JAN1960 15MAR2010
1 C 16MAR2010 11NOV2011
1 D 12NOV2011 31DEC9999
;
run;
data one; 
 set one;
 if todate='31DEC9999'd then todate='12dec2012'd;
run;
data two; 
 set two;
 if todate='31DEC9999'd then todate='12dec2012'd;
run;

data temp1;
 set one;
 a+1;
 do date1=fromdate to todate;
&amp;nbsp; output;
 end;
 format date1 date9.;
 drop fromdate&amp;nbsp; todate ;
 run;
 
 data temp2;
 set two;
 b+1;
 do date2=fromdate to todate;
&amp;nbsp; output;
 end;
 format date2 date9.;
&amp;nbsp; drop fromdate&amp;nbsp; todate ;
 run;
data temp;
&amp;nbsp; merge temp1 temp2;
run;

data temp;
 set temp;
 if a ne lag(a) or b ne lag(b) then group+1;
run;
data want;
 set temp;
 by group;
 retain temp;
 if first.group then temp=date1;
 if last.group then do;date1=temp;output;end;
 drop group a b temp;
run;



&lt;/PRE&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;&lt;/P&gt;&lt;P&gt;Ksharp&lt;/P&gt;&lt;/BODY&gt;&lt;/HTML&gt;</description>
      <pubDate>Mon, 10 Dec 2012 08:23:32 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Programming/Deriving-date-crossings-date-intersections-in-SCD2-tables/m-p/94835#M19980</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2012-12-10T08:23:32Z</dc:date>
    </item>
  </channel>
</rss>

