SCDs are well documented in data warehousing literature. Ralph Kimball has been particularly influential in describing dimensional modeling techniques for SCDs (see The Data Warehouse Toolkit, for example). Kimball has further coined different distinctions among ways to handle SCDs in a dimensional model. For example, a Type I SCD presents only the current view of a dimensional relationship, a Type II SCD preserves the history of a dimensional relationship, and so forth.
Example
The discussion below is based on an example sales organization that changes slowly in time as the territories are reorganized, e.g., Sales Reps switch Districts in time.As-is vs. As-was Analysis
One of the capabilities available with slowly changing dimensions is the ability to perform either "as-is" analysis or "as was" analysis.Case 1: Compound key with effective date/end date
One way to physically store a SCD is to employ Effective Date and End Date columns that capture the period of time during which each element relationship existed. In the example below, Sales Rep Jones moved from District 37 to District 39 on 1/1/2004 and Kelly moved from District 38 to 39 on 7/1/2004:LU_SALES_REP
sales_rep_id | sales_rep_name | district_id | eff_dt | end_dt |
1 | Jones | 37 | 1/1/1900 | 12/31/2003 |
2 | Smith | 37 | 1/1/1900 | 12/31/2099 |
3 | Kelly | 38 | 1/1/1900 | 6/30/2004 |
4 | Madison | 38 | 1/1/1900 | 12/31/2099 |
1 | Jones | 39 | 1/1/2004 | 12/31/2099 |
3 | Kelly | 39 | 7/1/2004 | 12/31/2099 |
FACT_TABLE
sales_rep_id | trans_dt | sales |
1 | 9/1/2003 | 100 |
2 | 9/10/2003 | 200 |
3 | 9/15/2003 | 150 |
1 | 3/1/2004 | 200 |
2 | 3/10/2004 | 250 |
3 | 3/15/2004 | 300 |
2 | 9/5/2004 | 125 |
3 | 9/15/2004 | 275 |
4 | 9/20/2004 | 150 |
LVW_CURRENT_ORG
select sales_rep_id, district_id
from LU_SALES_REP
where END_DT = '12/31/2099'
LVW_HIST_DISTRICT_SALES
select district_id, trans_dt, sum(sales) sales
from LU_SALES_REP L
join FACT_TABLE F
on (L.sales_rep_id = F.sales_rep_id)
where F.trans_dt between L.EFF_DT and L.END_DT
group by district_id, trans_dt
Define the following Attributes:
Sales Rep | @ID = sales_rep_id; @Desc = sales_rep_name |
Current District | @ID = district_id; @Desc = district_name |
Historical District | @ID = district_id; @Desc = district_name |
Date | @ID = date_id, trans_dt |
Month | @ID = MONTH_ID |
Sales | Expr: sales |
Sales | Sales: SUM(sales) |

As-Was Analysis
Users specify as-was analysis by using the Historical District attribute on reports:Report definition:
Historical District, Month, Sales
Resulting SQL:select a11.DISTRICT_ID DISTRICT_ID,
max(a13.DISTRICT_NAME) DISTRICT_NAME,
a12.MONTH_ID MONTH_ID,
sum(a11.SALES) WJXBFS1
from (select district_id, trans_dt, sum(sales) sales
from LU_SALES_REP L
join FACT_TABLE F
on (L.sales_rep_id = F.sales_rep_id)
where F.trans_dt between L.EFF_DT and L.END_DT
group by district_id, trans_dt
) a11
join LU_TIME a12
on (a11.TRANS_DT = a12.DATE_ID)
join LU_DISTRICT a13
on (a11.DISTRICT_ID = a13.DISTRICT_ID)
group by a11.DISTRICT_ID,
a12.MONTH_ID

As-Is Analysis
Users specify as-is analysis by using the Current District attribute on reports:Report definition:
Current District, Month, Sales
Resulting SQL:select a12.DISTRICT_ID DISTRICT_ID,
max(a14.DISTRICT_NAME) DISTRICT_NAME,
a13.MONTH_ID MONTH_ID,
sum(a11.SALES) WJXBFS1
from FACT_TABLE a11
join (select sales_rep_id, district_id
from LU_SALES_REP
where END_DT = '12/31/2099') a12
on (a11.SALES_REP_ID = a12.SALES_REP_ID)
join LU_TIME a13
on (a11.TRANS_DT = a13.DATE_ID)
join LU_DISTRICT a14
on (a12.DISTRICT_ID = a14.DISTRICT_ID)
group by a12.DISTRICT_ID,
a13.MONTH_ID

Case 2: New surrogate key for each changing element
A more flexible way to physically store a SCD is to employ surrogate keys and introduce new rows in the dimension table whenever a dimensional relationship changes. Another common characteristic is to include an indicator field that identifies the current relationship records. An example set of records is shown below.LU_SALES_REP
sales_rep_cd | sales_rep_id | sales_rep_name | district_id | current_flag |
1 | 1 | Jones | 37 | 0 |
2 | 2 | Smith | 37 | 1 |
3 | 3 | Kelly | 38 | 0 |
4 | 4 | Madison | 38 | 1 |
5 | 1 | Jones | 39 | 1 |
6 | 3 | Kelly | 39 | 1 |
FACT_TABLE
sales_rep_cd | Sales |
1 | 100 |
2 | 200 |
3 | 150 |
5 | 200 |
2 | 250 |
3 | 300 |
2 | 125 |
6 | 275 |
4 | 150 |
LVW_CURRENT_ORG select sales_rep_id, district_id
from LU_SALES_REP
where current_flag = 1
Define the following Attributes:
Sales Rep Surrogate | @ID = sales_rep_cd |
Sales Rep | @ID = sales_rep_id; @Desc = sales_rep_name |
Current District | @ID = district_id; @Desc = district_name |
Historical District | @ID = district_id; @Desc = district_name |
Date | @ID = date_id, trans_dt |
Month | @ID = MONTH_ID |
Sales | Expr: sales |
Sales | SUM(sales) |

As-Was Analysis
Report definition:Historical District, Month, Sales
Resulting SQL:select a12.DISTRICT_ID DISTRICT_ID,
max(a14.DISTRICT_NAME) DISTRICT_NAME,
a13.MONTH_ID MONTH_ID,
sum(a11.SALES) WJXBFS1
from FACT_TABLE a11
join LU_SALES_REP a12
on (a11.SALES_REP_CD = a12.SALES_REP_CD)
join LU_TIME a13
on (a11.TRANS_DT = a13.DATE_ID)
join LU_DISTRICT a14
on (a12.DISTRICT_ID = a14.DISTRICT_ID)
group by a12.DISTRICT_ID,
a13.MONTH_ID
As-Is Analysis
Report definition:Current District, Month, Sales
Resulting SQL:select a13.DISTRICT_ID DISTRICT_ID,
max(a15.DISTRICT_NAME) DISTRICT_NAME,
a14.MONTH_ID MONTH_ID,
sum(a11.SALES) WJXBFS1
from FACT_TABLE a11
join LU_SALES_REP a12
on (a11.SALES_REP_CD = a12.SALES_REP_CD)
join (select sales_rep_id, district_id
from LU_SALES_REP
where current_flag = 1
) a13
on (a12.SALES_REP_ID = a13.SALES_REP_ID)
join LU_TIME a14
on (a11.TRANS_DT = a14.DATE_ID)
join LU_DISTRICT a15
on (a13.DISTRICT_ID = a15.DISTRICT_ID)
group by a13.DISTRICT_ID,
a14.MONTH_ID

