Monday, June 14, 2021

Monday June 14, 2021

Leadership Development: A case study

Tigard GOLD is a local government organizing and providing leadership development programs. On Saturday June 12, 2021, I had the privilege of being a speaker and member of Panel Discussion. Great experience!

Come along with me in reflecting on the most recent good, takeaway, and lesson learned in leadership. In this article / blog, I will share with you my experience attending the Tigard GOLD, Leadership Skills (good, bad and ugly), and Leaders Development. Used the Bull Mountain Park development and myself, the past CEO and President of Friends Bull Mountain Park (FBMP) experiences as a case study.

Background:

I was born in Tehran and grew up in Shiraz and Ardekan, Iran. Moved to USA right after high school. Received my Bachelor and Master in New England, and Oregon Graduate Center for Post Master school. Have more than 3 decades of successful career in Electronic and Computers. Hardware and Software across different industries. An accomplished Information Technology and Computer Engineering Sr. manager, lead and principal engineer across different industries (Information Technology, Information systems, Computer Aided Design (CAD), manufacturing, federal and local governments, insurance, medical, pharmaceutical, dental, computer and electronics). 

Flag Day, June 14, 1989, I became US Citizen. In the Flag Day 2021, I have the honor of sharing my USA journey.

Irene (my wife) and I don’t have living children of our own. We are passionate for children, orphans, widows and single parents. Here are a few of our philanthropic endeavors.

  • Helped build Orphanage from Ground Up, between 1992 to 2016, Chauglamari India
  • Built Orphanage and Widow Wing, between 2016 – 2018, Pakistan and India border
  • Lead to build the Tigard 1st nature playground, between 2010-2019
  • Currently working with LBCC (Linn- Benton Community College) and Albany Rain Moneymakers. Support the future entrepreneurial and professionals in Engineering, Education and Faith based living. Started 2020.

Irene and I got involved in the Bull Mountain Park development during 2010. Bull Mountain Park land without amenities was publicly opened June 21 2012, by Councilor Dirksen (2012 Mayor). The Park was purchased by funds from the City of Tigard (COT) bond measure 34-181. The bond was restricted to purchasing parks.  The COT had very limited resources to develop and maintain parks, once purchased.

Friends of Bull Mountain Park (FBMP) was formed 1Q2011, more than 18 months prior to park purchase. FBMP is a grass roots non-profit volunteer group dedicated to nature, education, health, and developing Bull Mountain Park utilizing Green principles. All funds and projects for park development were raised and managed by the FBMP. FBMP led and completed dozens of projects since June 2012. Chairperson and President Toraj Khavari led FBMP from beginning and resigned 4Q2018 (completed development).

Bull Mountain Park (BMP) is a 10-acre Nature Park located on the East side of Bull Mountain in Tigard Oregon. All Park developments, activities and funds were accomplished, under direction of Toraj Khavari and FBMP’s board.
















The BMP soft trails start at multiple (6) entrances:

  1. SW Greenfield Drive,
  2. SW Benchview Place,
  3. SW Woodshire,
  4. SW Alpine Crest Way,
  5. SW Fernridge Terrace, and
  6. SW Ridgefield Ln.

For more information, please refer to the following resources.

City Of Tigard - https://www.tigard-or.gov/community/bull_mountain_park.php 

Facebook® - http://www.facebook.com/FEBMP

FBMP Blog Post: http://fbmpblogs.blogspot.com/

The BMP design was completed by Lango Hansen – 2015-2016.


The BMP construction was completed by GSE Inc. – 2016-2017.

Volunteers, FBMP, Lango Hansen, GSE, Local Businesses, Local and State Government agencies and a more resources came together to develop the BMP. The Nature Playground structure lumbers are from the Tigard Walnut Street expansion during 2016. 

Sunday October 15, 2017, the Bull Mountain Park was Dedicated. Friends Bull Mountain Park, Metro and COT opened the Tigard Nature Play area. Andrew Palau dedicated that Nature Play area under our nations flag, the Tigard first Nature Play Area was opened.

During 4Q2018 all the FBMP’s projects were capitalized to City of Tigard. Projects completed. Taxes were filed. Books were closed. On 1Q2019 the FBMP Inc. was dissolved. Today, the Bull Mountain Park is maintained by City of Tigard, with very active volunteers and community support.


More than a park:

Bull Mountain Park development was more than a “Park” philanthropy.

The Park development gave community Purpose through the following roles.

  • Engagement
  • Responsibility
  • Friendships started
  •  Relationships strengthened
  • Like objectives
  • Satisfaction and fulfillment
  • Equity and trust – inclusions
  • Vision and Joy with completion

* Picture was copied from Nick Bailey site.

The volunteers were brought together.

  • Old and young
  • Multi-generational
  • First generation Americans
  • Immigrants 
  • Refugees 
  • BIPOC (Black, Indigenous, (and) People of Color)
  • Business owners - Local and national
  • Government employees and administrators
  • Students and student clubs
  • Ongoing Maintenance by local volunteers

* Picture was copied from MAG University of Rochester

The FBMP during ten years, 2010 – 2019, developed the Bull Mountain Park. One of the best examples I can use for bringing local Tigard citizens, local small and large business owners, volunteers, Local / State and Fed. agencies, together to achieve Tigard’s first Nature Park for children of all ages to enjoy for years to come.

In 2019, Irene and I moved to Albany Oregon and are now involved with local community volunteerism.

Questions and Answers:

The Tigard Gold session challenged me to examine myself and my objectives during preparation to answer the future leaders’ questions.

Question 1 - What’s my advice for Tigard GOLD participants and future leaders in how they can find meaningful projects to get involved with in the community? Where can they learn about opportunities? How can they find projects that meet their interests?

Answer:

  • Go after what is right, good, honorable, and long lasting
  • Go after what you are passionate about
  • Don’t limit yourself to your present abilities and circumstances
  • Look for opportunities that are beneficial to you, your family and close communities. When your community, your family and you are benefiting, everyone will benefit.
  • Be visible, be available, go to events, volunteer, know your community and their needs. I consulted the the following sites often. 

Question 2 - Getting people engaged and involved is not easy. Talk about two or three of the main challenges and how you attempt to overcome them.

Answer: As community leaders know, accept and honor US Constitution. Focus on the “We the people”. I paraphrase it as, government for the people and people for government. With this knowledge and acceptance come two very solid take aways and responsibilities.

  1. Know when to be a Train Engine Leader, or Tugboat Leader or Lead From Behind (be a rudder). 
  2. Relationship Relationship … Relationship. Relationship is the only way to establish trust and enable “Hard Talk”.

* Pictures were copied from Internet and public sites

Let me explain

  • You know the Vision, Mission, have the future path / plan, have the future road well outlined and know, have all the resources and support, have all known risks identified, have contingency plan/s, lead ahead (lead like a Train Engine).
  • Come along a common goal or objective to achieve bigger objectives. Put your leadership and resources to work like as a Tugboat / help.
  • If the task / objective is huge, as a leader you are learning, or need to rely on a lot of resources and expertise to achieve the vision, lead from behind, be a Rudder leader.

Servant Leaders are leaders with philosophy in which the goal of the leader is to serve. A servant leader shares power, puts the needs of the others first and helps people develop and perform as comprehensively as possible. 

These styles of leadership require at minimum a) you know yourself, b) you can study the environment around you accurately, c) be agile and respond / change with short notice and d) be prepared to separate from non-performer resources.

Often, typical leaders’ mistakes are, the leader leads with wrong methods; Train Engine leadership when they should be Tugboat leader. Lead as Rudder when they should be a Tugboat leader (learn and help). Another common mistake, the leaders would not change their style in a timely manner. Good leaders want to know and hear from everyone. However, not everyone is a stakeholder nor always correct. Sometime the best solution is to separate from non-performers and incorrect resources. Leaders often want to have the hard talks. Hard talks without strong relationship lead to disaster (short lived the leader, hurt, perceived insults, etc.). Relationship comes by investing time and exposure. Before asking and leading, invest in human relationship and human trust banks.

Conflicts will come guarantied! Conflicts can be in resources, relationship, funds, $ management, etc. Plan for conflicts and have conflict resolution strategies. If you are dealing with capital funds, contingency budgets are your friends. 

Question 3 – What to watch for to see early success and leadership engagements? What do I recommend to new / young leaders?

Answer: To measure your leadership engagement, don’t focus on who are your followers. Watch your audiences, stakeholders and decision makers. Often, ask yourself the following questions.

  • Do I have my immediate family support?
  • Do I have audiences and platforms?
  • Am I listened to when I speak?
  • Do I listen enthusiastically?
  • Are my stakeholders willing to invest in me, my objectives, and visions?
  • Are my vision/s honorable, good, long lasting, realistic and timely?
  • Do I need to change my leadership method?

To answer, what do I recommend to new leaders, let me share with you the greatest advice I received and practice.

My education years were during the President Carter administration and at the peak of the America and Iran cold war.  Often in the university and town around us were potential unrest around different topics:

  • Women liberation - my body my choice
  • Homosexual agenda 
  • Unfair financial distribution in society 
  • Racism 
  • Inflation and high prices for fuel/oil and gasoline
  • Green movement 
  • Unemployment - Russia, China and 
  • Iran Hostage Crisis 

… Sound familiar! ….

My mentor at school at that time, watching and knowing what was going on, gave me the best guidance over a cup of coffee.  I am applying the guidance since then. Successfully, went through Hostage Crisis, Oklahoma Bombing, and 9/11 crisis. The US and Iran cold war continues. Often, Iran and Iranians are at the top of conspiracy list.

Now, more than 4 decades in USA, successful career, 32 years of American citizenship, fruitful philanthropy, you can see the results of my mentor’s advice, He said the following.

If you want to be a “Successful Person”, “Optimistic Warrior” for change … “Rise Above”.  

  • Do everything with truth, ethic and integrity
  • Get higher education and take it as far / high as you can
  • Work hard. In fact, outwork everyone around you.

 In this memorable, Flag Day 2021, I will challenge you to “Rise Above”. If an Iranian can do it, you can, too!

“Friends to the end”

Toraj 


Wednesday, January 30, 2019


Title: Error Handling and Notification in Microsoft SQL SSIS:

Version: 1.0.0119.01 – January 31, 2019.

Objective: Error log, reporting and email notification in Microsoft SQL.

Article Body: Recently, I had a business requirement to develop Error log, reporting and email notification in Microsoft SQL. The majority of my developments are in SQL Server Integration Services (SSIS) using Visual Studio (VS) Integrated Development Environment (IDE). In this case, SQL 2014 and VS2017.

Thanks to Microsoft SQL, SSIS and VS technologies, we have powerful tools to track runtime errors and email notification. There are multiple methods to implement runtime exception log, errors and email notification. The Event Handlers in SSIS are powerful solution.

1-      After you have created the SSIS Control Flow. In my case, incremental ETL between Production and Report server, select “Event Handlers” TAB.


2-      Select the “Request-New Records” in “Executable” and your desired error-handling module. Select “OnError” for “Event handler”.

3-      Drag and drop “Execute SQL Task” from SSIS Toolbox to Event Handlers TAB canvas. Open the “Execute SQL Task” and navigate to “Parameter Mapping”. Select Add and open the Variable Name drop down. There are large number of System Valuables available to us to log.

4-      In our case, I limited the System Valuables log. In the SQL server, create a log file.
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[ATICPEDPSYSEVENTLOG](
      [LogID] [int] IDENTITY(1,1) NOT NULL,
      [SourceID] [nvarchar](max) NULL,
      [PackageName] [nvarchar](max) NULL,
      [SourceName] [nvarchar](max) NULL,
      [ErrorDescription] [nvarchar](max) NULL,
      [CreatedDate] [datetime] NULL,
PRIMARY KEY CLUSTERED
(
      [LogID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

ALTER TABLE [dbo].[ATICPEDPSYSEVENTLOG] ADD  DEFAULT (getdate()) FOR [CreatedDate]
GO

5-      Cancel and reopen “Execute SQL Task” in Event Handlers TAB canvas. In the “General” TAB, Connect to your database, and past the following in the SQL Statement.

INSERT INTO [ATICPEDPSYSEVENTLOG] (SourceId, PackageName, SourceName, ErrorDescription)
VALUES (?, ?, ?, ?);

6-      Navigate to Parameters Mapping and Add System Source ID, Package Name, Source Name, and Error Description.


You are done.

<Hints>
-          For any other Evet Handlers, copy and paste the “Execute SQL Task” in Event Handlers TAB canvas for your desired SQL module.
-          After the SSIS package deployment in SQL Server, use standard SQL job scheduling and email notification.  For more deployment, instruction refer to “Microsoft Visual Studio, Dynamics AX ETL SSIS Package Deployment, SQL Security”.
-          The SQL Event Log, in my case, ATICPEDPSYSEVENTLOG, contains details log information for subsequent analysis.

If you enjoy what you do; you never work a day in your life.

Cheers, Toraj

References:
·         SSIS Event Handlers Basics, https://www.red-gate.com/simple-talk/sql/ssis/ssis-event-handlers-basics/


Tuesday, January 9, 2018

Title: Type 2 Slow Changing Dimension Tree design pattern data structure:

Version: 1.0.0109.01 – January 9, 2018.

Objective: Architect, design, and implementation review for type 2 Slow Changing Dimension (SCD) Tree design pattern data structure. It has a simple Parent and Child input SharePoint list. Uses SharePoint List OData ETL Extract, SQL ETL, and DAX Tabular Cube in SQL DataStore Enterprise Data Warehouse (EDW).

Article Body: Very recently, I faced the business need to implement a solution collection to support type 2 SCD Tree design pattern data structure. The requirements are as follows.
-          Minimum to no data stewards support to manipulate the data or report after deployment.
-          Repeatable across different parent-child Tree structure data sets. E.g., Customer Hierarchy, Sales Hierarchy, Region Hierarchy, Product Configuration Hierarchy, Territory Manager Hierarchy, etc.
-          Proof of concept must be applied to a practical business need.
-          Use SQL environment (SSIS, SSAS, and SSRS), Tabular Cube, and Corporate SharePoint.
-          Supports incremental builds based on the scheduled execution frequency.

In this article, the readers should be knowledgeable about Tree Data Structure, Parent Child Adjacency List, SharePoint List, Microsoft Visual Studio (VS), SQL Server Integration Services (SSIS), SQL Analysis Server (SSAS), SQL Tabular Modeling (SSAS), and Data Analysis eXpressions (DAX) language.

Utilized Parent Child Adjacency SharePoint list to communicate the tree hierarchy between end users and the EDW system.  The SharePoint list is maintained by Data Stewards. The list has limited access rights constraints controlled by SharePoint security and the site administrator. The list uses SharePoint list row IDs to minimize users’ impacts. Data Stewards edit and view texts (e.g., Customer Name). The SharePoint list drop down assists to identify the Child node’s Parent for a given row. Parent Child Adjacency SharePoint list maintenance is made easier. The SharePoint list IDs, are hidden from the Data Stewards. The SharePoint list IDs communicate the data structure’s details to EDW.  SharePoint list configuration is out of scope of this article.

I outlined how to use OData technology to ETL extract the SharePoint list data from SharePoint to EDW Data Store in the following article. It is a generic solution that can be applied to any tree data structure data set.

For practical application, I am applying the type 2 SCD Tree design pattern data structure to Customer Hierarchy. Customer Hierarchy is a tree structure summarizing sales based on Customer Type, e.g., Domestic, International, School, Governments. I refer to Type 2 SCD Customer Hierarchy as CHN (Customer Hierarchy Normalized).

<Aside> Normalized! This is a complex algorithm; a solution collection across multiple technologies. There are no off-the-shelves solution I have found, yet. Type 2 SCD Customer Hierarchy at the end of my design is a scalable table with incremental add-on rows. The incremental add-ons are based on frequency of production execution schedule. The incremental add-on makes it normalized. A Tabular Cube DAX dynamic Type 2 SCD Child-Parent resolve; built on off-the-shelves Type 2 SCD practices will work, too. Thereafter, the data presentation layer, in our case power BI and SSRS, must perform the heavy lifting that I perform in SQL. However, presentation layer solution requires different business requirements.

There might be multiple design strategies to implement CHN. The following is my current solution collection. Please feel free to share your inputs and potential enhancements.  

Best practices adhere to as follows.
-          Dynamic table creation in memory can become performance disadvantaged very fast with large tables. Use temp tables as much as possible to minimize SQL execution time memory demand.
-          Create and destroy temp tables in the same work flow.
-          Have temp tables in their own database. In this project referred to “DataStore_Utility” database.
-          Keep production DBA happy.

The CHN table structure as follows.
CREATE TABLE [DataStore].[dbo].[CustomerHierarchyNormalized](
            [NameKey] [int] IDENTITY(1,1) NOT NULL,            -- Key reference for Name
            [ParentNameKey] [int] NULL, -- Key reference for Parent Name
            [Name] [nvarchar](100) NULL,           -- Name
            [NameID] [int] NULL, -- Name ID assigned by SharePoint
            [ParentNameID] [int] NULL,   -- Parent Name ID using SharePoint ID.
            [NameCustomerAccountID] [nvarchar](20) NULL,    -- A data steward and fact table reference
            [Effectivity] [date] NULL,       -- Start date for the valid information. Part of Type 2 SCD need.
            [Expiry] [date] NULL) -- End date for the valid information. Part of Type 2 SCD need.

ETL Extract the SharePoint adjacency list assigned ID to staging and data store. They will be needed during normalization, good source of debugging, and production issues route cause analysis.

Sample data as follows.


The solution has three major components.
-          SharePoint and EDW staging ETL Extract, covered in my prior article. Out of scope of this article.
-          Data Store ETL
-          Tabular Cube model

Data Store ETL
VS preparation steps.
1.      If you do not have a project collection in VS solution, add a new Business Intelligence, Integration, Integration Service, and name it “<YourCompanyName>.EDW.ETL.Datastore”. In my case it is “Adec.EDW.ETL.Datastore”.
2.      Add a “CustomerHierarchyNormalized.dtsx” project in “Adec.EDW.ETL.Datastore”.
3.      Add all the needed Connection Managers.
4.      Henceforth, perform all the tasks in VS “CustomerHierarchyNormalized.dtsx” project.

The data store ETL solution has multiple steps.
1-      Prepare Temp Tables
2-      Track changes and ETL Extract the Adjacency SharePoint list data
3-      Update information for changes
4-      Get the changes for descendants
5-      Update the CHN table
6-      Clean up


1-      Prepare Temp Tables.
a.       Add Sequence Container. Rename it “Prepare Temp Tables Sequence Container”. In the Sequence Container add four Execute SQL Tasks. Rename them appropriately.

b.      In each Execute SQL Tasks connect to “DataStore_Utility” database. Copy and paste the following code in the SQL Tasks, appropriately.

1st – Create a table to have initial CHM NameKey.
IF OBJECT_ID('dbo.zTmp_CustomerHierarchyNormalized_NameKey')IS NOT NULL
      BEGIN
                  TRUNCATE TABLE [dbo].[zTmp_CustomerHierarchyNormalized_NameKey];
      END
ELSE
      BEGIN
                  CREATE TABLE [dbo].[zTmp_CustomerHierarchyNormalized_NameKey](
                              [NameKey]                  INT      NULL)
                  ON [PRIMARY];
      END

 Next - Create a table to have the added / changed CHM NameKey.

IF OBJECT_ID('dbo.zTmp_CustomerHierarchyNormalized_Added')IS NOT NULL
      BEGIN
                  TRUNCATE TABLE [dbo].[zTmp_CustomerHierarchyNormalized_Added];
      END
ELSE
      BEGIN
                  CREATE TABLE [dbo].[zTmp_CustomerHierarchyNormalized_Added](
                              [NameKey]                  INT      NULL)
                  ON [PRIMARY];
      END

Next – Create a table to contain the added / changed CHN information.
IF OBJECT_ID('dbo.zTmp_CustomerHierarchyNormalized_Updates')IS NOT NULL
 BEGIN
  TRUNCATE TABLE [dbo].[zTmp_CustomerHierarchyNormalized_Updates];
 END
ELSE
 BEGIN
  CREATE TABLE [dbo].[zTmp_CustomerHierarchyNormalized_Updates](
      [NameKey] [int] NOT NULL,
      [ParentNameKey] [int] NULL,
      [Name] [nvarchar](100) NULL,
      [NameID] [int] NULL,
      [ParentNameID] [int] NULL,
      [NameCustomerAccountID] [nvarchar](20) NULL,
      [Effectivity] [date] NULL,
      [Expiry] [date] NULL)
  ON [PRIMARY];
 END

Next - Create a table to contain the added / changed CHN descendants.
IF OBJECT_ID('dbo.zTmp_CHN_Updates_Descendants')IS NOT NULL
 BEGIN
  TRUNCATE TABLE [dbo].[zTmp_CHN_Updates_Descendants];
 END
ELSE
 BEGIN
  CREATE TABLE [dbo].[zTmp_CHN_Updates_Descendants](
      [NameKey] [int] NOT NULL,
      [ParentNameKey] [int] NOT NULL,
      [Name] [nvarchar](100) NULL,
      [NameID] [int] NULL,
      [ParentNameID] [int] NULL,
      [NameCustomerAccountID] [nvarchar](20) NULL,
      [Effectivity] [date] NULL,
      [Expiry] [date] NULL)
 END

2-      Track changes and ETL Extract the Adjacency SharePoint list data
a.       Track the CHM present state. After Sequence Counter add a Data Flow Task and rename it “Track Existing CustomerHierarchyNormalized Data Flow Task”.

Hint:
The temp tables are created during run time. Therefore, all the SSIS Data Flow interacting with temp tables “Delay Validation” proprieties are set to True.

Add an OLE DB Source. Rename it “Set the NameKey OLE DB Source”. With the OLE DB connection manager set to Datastore, select SQL command, and copy / paste the following code in SQL command.

SELECT [NameKey]
  FROM [dbo].[CustomerHierarchyNormalized]


Add an OLA DB Destination. Rename it “Insert Destination”. Connect the “Set the NameKey OLE DB Source” to “Insert Destination”. Save the information in the temp table [dbo].[zTmp_CustomerHierarchyNormalized_NameKey].


b.      Get the SharePoint adjacency list and persist the data steward’s changes from the previous execution time. In the DataStore_Utility create a view, as follows.

CREATE VIEW [dbo].[UpdateCustomerHierarchyNormalizedInit]
      AS
      /*** Get Parent and Child information for CustomerHierarchyNormalized  from SharePoint List ***/
      Select
                  cast(CustomerHierarchy.[Name] as nvarchar(100)) AS 'Name'
                  , CustomerHierarchy.[ID] AS 'NameID'
                  , CASE WHEN parent.[Name] in ('-1', 'All') THEN NULL
                              ELSE CustomerHierarchy.[ParentNameID]
                  END AS [ParentNameID]
                  ,cast(CustomerHierarchy.[CustomerAccount] AS nvarchar(20)) AS 'NameCustomerAccountID'
      FROM [DataStore_Staging].[sp].[CustomerHierarchy] CustomerHierarchy
      inner join [DataStore_Staging].[sp].[CustomerHierarchy] parent
                  on parent.ID=CustomerHierarchy.ParentNameID
      where CustomerHierarchy.Name not in ('All')
GO

<Aside>  ‘-1’ and ‘All’ are used in SharePoint adjacency list to assist the data stewards visually and minimize errors. While it is needed for legacy type 2 SCD, there is no need for them in this design for the presentation layers. The users prefer not to see ‘-1’ and ‘All’ in the reports, Power BI, and Pivot tables.

After “Track Existing CustomerHierarchyNormalized Data Flow Task” add a “Get SharePoint Data Flow Task SCD”.

Add an OLE DB Source. Rename it “CustomerHierarchyNormalized Row Count OLE DB Source”. With the OLE DB connection manager set to DataStore_Utility, select SQL command, and copy / paste the following code in SQL command.

SELECT [Name]
      ,[NameID]
      ,[ParentNameID]
      ,[NameCustomerAccountID]
  FROM [dbo].[UpdateCustomerHierarchyNormalizedInit]

After “CustomerHierarchyNormalized Row Count OLE DB Source” add SSIS tool Slowly Changing Dimension. Its Insert Destination is the [DataStore].[dbo].[CustomerHierarchyNormalized].


c.       Track the added / change CHN data. After “Get SharePoint Data Flow Task SCD” add a Data Flow Task and rename it “Track Added CustomerHierarchyNormalized Data Flow Task”.

Add an OLE DB Source. Rename it “Set the NameKey OLE DB Source”. With the OLE DB connection manager set to Datastore_Utility, select SQL command, and copy / paste the following code in SQL command.

SELECT NowList.[NameKey]
  FROM [dbo].[zTmp_CustomerHierarchyNormalized_NameKey] AS InitList
  INNER JOIN [DataStore].[dbo].[CustomerHierarchyNormalized] AS NowList
      ON NowList.NameKey=InitList.[NameKey]
  where  InitList.[NameKey] != NowList.NameKey

Add an OLA DB Destination. Rename it “Insert Destination”. Connect the “Set the NameKey OLE DB Source” to “Insert Destination”. Save the information in the temp table [dbo].[zTmp_CustomerHierarchyNormalized_Added].


At this point during the package execution the following temp table contains the NameKey of the added changes to Customer Hierarchy Normalized table. Deleted rows are handled with Expiry date. There are no delete rows in the SCD structure. Will address updates at later time in this article.
[DataStore_Utility].[dbo].[zTmp_CustomerHierarchyNormalized_Added]

3-      Update information for changes has two components.
a.       The added rows do not have the correct ParentNameKey. In this step, update the ParentNameKey in the following table.
[DataStore].[DBO].[CustomerHierarchyNormalized]

In the DataStore_Utility data base create a new Stored Procedure as follows.

USE [DataStore_Utility]
GO

CREATE PROCEDURE [dbo].[sp_CustomerHierarchyNormalized_UpdateAddedParentNameKey]
AS
      UPDATE Child
      set ParentNameKey = Parent.NameKey         
      FROM [DataStore].[DBO].[CustomerHierarchyNormalized]  Child 
                  INNER JOIN [DataStore].[DBO].[CustomerHierarchyNormalized] Parent
ON Child.ParentNameID = Parent.NameID   
                  INNER JOIN [dbo].[zTmp_CustomerHierarchyNormalized_Added] Added
ON Added.NameKey=Child.NameKey
GO

In VS after “Track Added CustomerHierarchyNormalized Data Flow Task” add “Update Added ParentNameKey Execute SQL Task”. Edit the SQL task and set the SQL Statement as follows.
EXEC [dbo].[sp_CustomerHierarchyNormalized_UpdateAddedParentNameKey];


b.      Create a new view as follows.
USE [DataStore_Utility]
GO

CREATE VIEW [dbo].[UpdateCustomerHierarchyNormalizedUpdates]
      AS
      SELECT CHNAdded.[NameKey]
                    ,[ParentNameKey]
                    ,[Name]
                    ,[NameID]
                    ,[ParentNameID]
                    ,[NameCustomerAccountID]
                    ,[Effectivity]
                    ,[Expiry]
        FROM [DataStore].[dbo].[CustomerHierarchyNormalized] AS CHN
        INNER JOIN [dbo].[zTmp_CustomerHierarchyNormalized_Added] AS CHNAdded on
                  CHNAdded.[NameKey]=CHN.NameKey
GO

After “Update Added ParentNameKey Execute SQL Task” add “Get Added CustomerHierarchyNormalized Details Flow Task”. Add “Set the NameKey OLE DB Source”. Select DataStore_Utility, SQL command, and copy / paste the following code.

SELECT [NameKey]
      ,[ParentNameKey]
      ,[Name]
      ,[NameID]
      ,[ParentNameID]
      ,[NameCustomerAccountID]
      ,[Effectivity]
      ,[Expiry]
  FROM [dbo].[UpdateCustomerHierarchyNormalizedUpdates]


After “Set the NameKey OLE DB Source” add “OLE DB Destination” and save the data in the following table.
[DataStore_Utility].[dbo].[zTmp_CustomerHierarchyNormalized_Updates].

4-      Get the changes for descendants. In this section, identify the descendants for the added changes. In the DataStore_Utility data base create a new Stored Procedure as follows.

USE [DataStore_Utility]
GO

CREATE PROCEDURE [dbo].[sp_CustomerHierarchyNormalized_AddedUpdatesDescendants]
AS
      DECLARE @SCDDate datetime = getdate();

      WITH OrgUpdates AS
      (
                              --Our Changed Records
                  SELECT Updates.[NameKey]
                                          ,Updates.[ParentNameKey]
                                          ,Updates.[Name]
                                          ,Updates.[NameID]
                                          ,Updates.[ParentNameID]
                                          ,Updates.[NameCustomerAccountID]
                                          ,Updates.[Effectivity]
                                          ,Updates.[Expiry]
                              FROM [dbo].[zTmp_CustomerHierarchyNormalized_Updates] AS Updates
                                          LEFT JOIN [DataStore].[DBO].CustomerHierarchyNormalized AS CHN
                                                      on Updates.NameID = CHN.NameID
                                          and @SCDDate > CHN.Effectivity
                                          and ISNULL(CHN.Expiry, '') = ''

                  UNION ALL
                              -- Find Descendants
                              SELECT CHN.[NameKey]
                                                      ,CHN.[ParentNameKey]
                                                      ,CHN.[Name]
                                                      ,CHN.[NameID]
                                                      ,CHN.[ParentNameID]
                                                      ,CHN.[NameCustomerAccountID]
                                                      ,CHN.[Effectivity]
                                                      ,CHN.[Expiry]
                              FROM [DataStore].[DBO].CustomerHierarchyNormalized AS CHN
                                          INNER JOIN OrgUpdates AS OrgUpdates
                                                      on CHN.ParentNameID = OrgUpdates.NameID
                                                      and @SCDDate > CHN.Effectivity
                                                      and ISNULL(CHN.Expiry, '') = ''
      )

      INSERT INTO [dbo].[zTmp_CHN_Updates_Descendants]
      SELECT DISTINCT [NameKey]
                              ,[ParentNameKey]
                              ,[Name]
                              ,[NameID]
                              ,[ParentNameID]
                              ,[NameCustomerAccountID]
                              ,[Effectivity]
                              ,[Expiry]
      FROM OrgUpdates

      Delete CUD
      from [dbo].[zTmp_CHN_Updates_Descendants] CUD
      Inner JOIN [dbo].[zTmp_CustomerHierarchyNormalized_Updates] CHN on CHN.NameKey=CUD.NameKey

GO

In VS after “Get Added CustomerHierarchyNormalized Details Flow Task” add “Get Added Updates_Descendants Details Flow Task”. Edit the SQL task and set the SQL Statement as follows.
EXEC [dbo].[sp_CustomerHierarchyNormalized_AddedUpdatesDescendants];


5-      Update the CHN table. There are three steps.
a.       Expire effected data in CHN table. In the DataStore_Utility data base create a new Stored Procedure as follows.

USE [DataStore_Utility]
GO

CREATE PROCEDURE [dbo].[sp_CustomerHierarchyNormalized_ExireEffected]
AS
      DECLARE @SCDDate datetime = getdate();
      UPDATE CHN
      SET CHN.[Expiry] = @SCDDate
      FROM [dbo].[zTmp_CHN_Updates_Descendants] AS CPD
                  INNER JOIN [DataStore].[DBO].CustomerHierarchyNormalized AS CHN on CPD.NameKey = CHN.NameKey
                                                      and @SCDDate > CHN.Effectivity
                                                      and ISNULL(CHN.Expiry, '') = ''
      UPDATE [dbo].[zTmp_CHN_Updates_Descendants]
      SET [Effectivity] = @SCDDate
GO

In VS after “Get Added Updates_Descendants Details Flow Task” add “Expire Effected ROWS Execute SQL Task”. Edit the SQL task and set the SQL Statement as follows.
EXEC [dbo].[sp_CustomerHierarchyNormalized_ExireEffected];


b.      Insert New CustomerHierarchyNormalized Details Flow Task. After “Expire Effected ROWS Execute SQL Task” add Data Flow “Insert New CustomerHierarchyNormalized Details Flow Task”. Add “Get New OLE DB Source”, with DataStore_Utility and SQL command as follows.

SELECT [ParentNameKey]
      ,[Name]
      ,[NameID]
      ,[ParentNameID]
      ,[NameCustomerAccountID]
      ,[Effectivity]
      ,[Expiry]
  FROM [dbo].[zTmp_CHN_Updates_Descendants]

Add OLE DB Destination after Get New OLE DB Source. Set the destination to CHN table as follows and map the fields.
[DataStore].[dbo].[CustomerHierarchyNormalized]

The Destination Name key is not mapped to anything. It is auto incremented primary key.

c.       Updates Current ParentNameKey Execute SQL Task. The effected rows ParentNameKey require updates.

Add a new Stored Procedure as follows.
USE [DataStore_Utility]
GO

CREATE PROCEDURE [dbo].[sp_CustomerHierarchyNormalized_UpdatesParentNameKey]
AS
      DECLARE @SCDDate datetime = getdate();
      UPDATE Child
      SET ParentNamekey = Parent.NameKey
      FROM [DataStore].[dbo].[CustomerHierarchyNormalized] AS Child
                  INNER JOIN [DataStore].[dbo].[CustomerHierarchyNormalized] AS Parent
                  ON Child.ParentNameID=Parent.NameID
      WHERE @SCDDate > Child.Effectivity
                  and ISNULL(Child.Expiry, '') = ''
                  and @SCDDate > Parent.Effectivity
                  and ISNULL(Parent.Expiry, '') = ''
GO

After “Insert New CustomerHierarchyNormalized Details Flow Task” add “Updates Current ParentNameKey Execute SQL Task”. Set the SQL Statement as follows.

EXEC [dbo].[sp_CustomerHierarchyNormalized_UpdatesParentNameKey];


6-      Clean up
a.       Add Sequence Container. Rename it “Clean up Sequence Container” after “Updates Current ParentNameKey Execute SQL Task”. In the Sequence Container add four Execute SQL Tasks. Rename them appropriately.
b.      In each Execute SQL Tasks connect to “DataStore_Utility” database. Copy and paste the following code in the SQL Tasks, appropriately.

First – Cleanup table for initial CHM NameKey.
IF OBJECT_ID('zTmp_CustomerHierarchyNormalized_NameKey')IS NOT NULL
DROP TABLE [dbo].[zTmp_CustomerHierarchyNormalized_NameKey];

 Next - Cleanup table that have the added / changed CHM NameKey.
IF OBJECT_ID('zTmp_CustomerHierarchyNormalized_Added')IS NOT NULL
 DROP TABLE [dbo].[zTmp_CustomerHierarchyNormalized_Added];

Next – Cleanup table that contains the added / changed CHN information.
IF OBJECT_ID('zTmp_CustomerHierarchyNormalized_Updates')IS NOT NULL
 DROP TABLE [dbo].[zTmp_CustomerHierarchyNormalized_Updates];

Next - Create a table to contain the added / changed CHN descendants.
IF OBJECT_ID(' zTmp_CHN_Updates_Descendant')IS NOT NULL
 DROP TABLE [dbo].[zTmp_CHN_Updates_Descendant];

For testing, feel free to use the following data for CHN.


The “Data Store ETL” selection is complete. Let us focus on model, next.

Tabular Cube model
The tabular data model must be updated to include the CHN table and its appropriate measures. In this section, let us explore the solution together.

In the tabular model, in my case “Adec.EDW.Datastore.Tabular.Finance” import the following table.
[DataStore].[dbo].[CustomerHierarchyNormalized]

In the model table view add the following columns. Copy and paste the DAX code for each added column, respectively.

Identify the nodes path, AKA, note heritage. Add Column “Path”.
DAX Code as follows.
=path('Customer Hierarchy Normalized'[NameKey], 'Customer Hierarchy Normalized'[ParentNameKey])

Calculate the depth of node’s hierarchy. Add Column “HierarchyDepth”.
DAX Code as follows.
=PATHLENGTH('Customer Hierarchy Normalized'[Path])

The level 1 through 8 identifies the node parent. Leaf through trunk of tree. If more than eight levels are needed additional level need to be added to the model. Add Column “Level1”.
DAX Code as follows.
=LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 1, 1))

Add Column “Level2”.
DAX Code as follows.
=IF (
    'Customer Hierarchy Normalized'[HierarchyDepth] >= 2,
                  LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 2, 1)),
    'Customer Hierarchy Normalized'[Level1]
)

Add Column “Level3”.
DAX Code as follows.
= IF (
    'Customer Hierarchy Normalized'[HierarchyDepth] >= 3,
                  LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 3, 1)),
    'Customer Hierarchy Normalized'[Level2]
)

Add Column “Level4”.
DAX Code as follows.
=
IF (
    'Customer Hierarchy Normalized'[HierarchyDepth] >= 4,
                  LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 4, 1)),
    'Customer Hierarchy Normalized'[Level3]
)


Add Column “Level5”.
DAX Code as follows.
= IF (
    'Customer Hierarchy Normalized'[HierarchyDepth] >= 5,
                  LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 5, 1)),
    'Customer Hierarchy Normalized'[Level4]
)

Add Column “Level6”.
DAX Code as follows.
= IF (
    'Customer Hierarchy Normalized'[HierarchyDepth] >= 6,
                  LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 6, 1)),
    'Customer Hierarchy Normalized'[Level5]
)

Add Column “Level7”.
DAX Code as follows.
= IF (
    'Customer Hierarchy Normalized'[HierarchyDepth] >= 7,
                  LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 7, 1)),
    'Customer Hierarchy Normalized'[Level6]
)

Add Column “Level8”.
DAX Code as follows.
= IF (
    'Customer Hierarchy Normalized'[HierarchyDepth] >= 8,
                  LOOKUPVALUE (
                              'Customer Hierarchy Normalized'[Name],
                              'Customer Hierarchy Normalized'[NameKey],
                              PATHITEM ( 'Customer Hierarchy Normalized'[Path], 8, 1)),
    'Customer Hierarchy Normalized'[Level7]
)

Add Column “IsLeaf”.
DAX Code as follows.
= CALCULATE (
COUNTROWS('Customer Hierarchy Normalized'),
All('Customer Hierarchy Normalized'),
'Customer Hierarchy Normalized'[ParentNameKey] =
EARLIER('Customer Hierarchy Normalized'[NameKey])
) = 0

In the model diagram view, build the cube hierarchy for Customer Hierarchy Normalized. Connect the Customer Hierarchy Normalized NameKey to the date dependent fact table key for the given row.

The fact table lookup must be date dependent. In my case, I use the following lookup logic.

"select chn.Namekey as CustomerHierarchyKey,
                  chn.Name,
                  chn.NameCustomerAccountID,
                  dd.DateKey
from [dbo].[CustomerHierarchyNormalized] chn
       INNER JOIN DateDimension dd ON dd.Date >= chn.EFFECTIVITY            
       AND (dd.Date < chn.EXPIRY OR chn.EXPIRY IS NULL)                   
       AND dd.Date <= '" + (DT_WSTR,25)(DT_DBDATE)@[User::MaxDate] +"'
       AND dd.Date >= '" + (DT_WSTR,25)(DT_DBDATE)@[User::MinDate] +"'"

The max and min dates are resolved during runtime. Use Expression Builder to create the date dependent look up. Thereafter, use the variable for lookup. In my case, User:CustomerHierarchySQLCommand.


The Event Handlers in SSIS package are very powerful and easy to use. I use the SQL standard system variables to track and report error bread crumbs. At some later time, the error table can be used for fault tolerant system architecture and system error’s alerts. At this point, it can be a good source for the production support team to perform some route cause analysis before contacting me.
 

Type-2 SCD Tree Design Pattern is needed in multiple areas. The above solution collection is a good and solid start.

Special thanks to Tracie Wagner with her right questions and Robert Lefebvre encouragement to follow good productionability practices.

Happy designing. Make the computer dance!

Enjoy what you do, you never work a day in your life.

Cheers, Toraj

<Aside> Jan-16-2018. Applied the same pattern to the Sales Region Hierarchy Type 2 Slow Changing Dimension (SCD). The pattern proved to be very repeatable and scalable.

References: