Party Model: Tracking people, companies, and internal company groups along with their relationship to one another and their contact points is an absolute need for any one. This model presents the standard concept that you will see in the modelling world called the 'Party Model'. Its important to have a generalization concept the covers both person and organization called the Party because you will come across many business concepts in which either a person or group can have a relationship with. Examples are Contracts, Memberships, Services, Products, Sales, and Shipment. This will enable a relationship to just relate to the Party entity rather than making a relationship for both Person and Organization. From a conceptual modeling purposes this makes the model easy to read which is the main point of what conceptual models are for. But generalization concepts may not necessarily be the best design from a physical performance perspective if the generation results in a hot table becoming a bottle next in the system. The needs of the physical world should prevail.
Note 1: In this model Contact Points only can exist if they are associated with a party. But if you are a postal service the address is important all on its own and can exist without a party. So you will need to make the necessary modifications. But that usually is a corner case from my experience.
Note 2: This model demonstrates role naming: Person ID from the Human Resource Entity is really a role name for a Party ID from the Party Entity. Organization ID from the Human Resource Entity is really a role name for a Party ID from the Party Entity.
Note 3: You will notice that the Human Resource Contact Point Entity has a suggested Primary Key of Organization ID, Person ID, Contact ID in which Person ID is BOLD. Person ID is an example of key folding in which the Party ID from Contact Point folds into Person ID from the Human Resource Entity. I personally like to call this a data harmonic. Hope that helps you understanding some advanced modeling techniques that are demonstrated in this model.
Please see (Baseline Conceptual Models Commentary) for further details on what conceptual models are to be used for.
Personal Technical Diary on Data & Process Models, Data Warehousing, Planning, Techniques, and Memoirs.
Thursday, January 02, 2014
Baseline Conceptual Models: Scheduled Event Model
Scheduled Event Model: Events, Anniversaries, Holidays, Vacations, Meeting Schedules, Pipeline Execution Schedule, and Appointments are just a few types of events people, companies, or machines need to track. This conceptual model works with the Calendar Model and Party Model to cover the ability to create an event calendar someone can add to there personal calendar to show the scheduled events for a given subject area, group, or person. Example: National Holidays, Company Paid Holidays, Company Wide Events, Personal Vacation Schedule, Birthday List, etc.... In application I can add to my personal calendar the list of national holidays for the U.S., the Company Paid Holidays, Company Events, and the Pipeline Execution Schedule to keep me informed.
Please see (Baseline Conceptual Models Commentary) for further details on what conceptual models are to be used for.
Please see (Baseline Conceptual Models Commentary) for further details on what conceptual models are to be used for.
Baseline Conceptual Models: Calendar Model
Calendar Model: Calendars are ubiquitous and required by any society. Usually there are two calendars for a medium-to-large business: Gregorian and Fiscal. This model should allow a business to create any type of calendar including Gregorian, Fiscal, Chinese, Hindu, Hebrew, or Astrological. It does dictate that you should always have a year, month, week, and day concepts with an optional quarterly concept.
Note 1: The Quarter concept could be based on a 13 week cycle(52/4) for each year or could be based on 3 month cycle (12/4) for each year.
Note 2: The Reserved Name Space is pretty straight forward as it represents for any type of calendar the names for each month and the names for each day of the week. But the year name space may sound weird until you think of Chinese or astrological calendars in which the year name of a name of a constellation. Example: Year of the Dragon.
Please see (Baseline Conceptual Models Commentary) for further details on what conceptual models are to be used for.
Note 1: The Quarter concept could be based on a 13 week cycle(52/4) for each year or could be based on 3 month cycle (12/4) for each year.
Note 2: The Reserved Name Space is pretty straight forward as it represents for any type of calendar the names for each month and the names for each day of the week. But the year name space may sound weird until you think of Chinese or astrological calendars in which the year name of a name of a constellation. Example: Year of the Dragon.
Please see (Baseline Conceptual Models Commentary) for further details on what conceptual models are to be used for.
Thursday, April 04, 2013
How To: Sequence SQL Tables based on Dependency
There are many times in which you may need to know the sequence of tables based on dependencies in order to process data without foreign constraint issues and without turning off those constraints. You'll need a list of tables from the most dependent table to the least dependent table. The process below provides an example in how to provide such a list.
CREATE FUNCTION dbo.GetTableSequence ()
RETURNS @Temp2 Table
( Sequence int Primary key Identity
, ObjectID int
, SchemaName sysname
, TableName sysname)
AS
BEGIN
DECLARE @Temp1 Table
( ForeignObjectID int
, ForeignSchemaName sysname
, ForeignTableName sysname
, ObjectID int
, SchemaName sysname
, TableName sysname)
-----------------------------------------------------
-- Identify Tables with Depedency on another table
-----------------------------------------------------
Insert into @Temp1 (ForeignObjectID, ForeignSchemaName, ForeignTableName, ObjectID, SchemaName, TableName)
Select Distinct O.Object_ID as ForeignObjectID, Cast(schema_Name (o.Schema_ID) as sysname) as ForeignSchemaName, cast(o.name as sysname) as ForeignTableName,
o2.Object_ID as ObjectID, Cast(schema_Name (o2.Schema_ID) as sysname) as SchemaName, cast(o2.Name as sysname) as TableName
From sys.foreign_keys f
Join sys.objects o
on f.referenced_object_id = o.object_id
Join sys.objects o2
on f.parent_object_id = o2.object_id
Where O.Name <> O2.Name -- Exclude Child-Parent Relationships (These causes infinite loops)
---------------------------------------------------------------------------------------
-- Recursively Identify Tables from Most Dependent to least Dependent
---------------------------------------------------------------------------------------
;
with RecursionCTE (ObjectId, SchemaName, TableName)
as
(-- First Get all tables that are not dependent on any other table. (Table can depend on them but not the other way a round)
SELECT T.ObjectID, T.SchemaName, T.TableName
FROM (Select Distinct O.Object_ID as ObjectID
, Cast(schema_Name (o.Schema_ID) as sysname) as SchemaName
, Cast(o.name as sysname) as TableName
From sys.objects o
Join sys.columns c
on c.object_id = o.object_id
Where o.type = 'u') as T
LEFT OUTER JOIN sys.foreign_key_columns sfk
ON sfk.referenced_object_id = T.ObjectID
Where sfk.referenced_object_id is null
union all
-- Now recursively work through all dependencies
select R1.ForeignObjectID, R1.ForeignSchemaName, R1.ForeignTableName
FROM @Temp1 as R1
join RecursionCTE as R2 on R1.TableName = R2.TableName
)
Insert Into @Temp2(ObjectId, SchemaName, TableName)
select ObjectID, SchemaName, TableName
from RecursionCTE
;
----------------------------------------------------
-- Delete Duplicates but keep the very last entry
----------------------------------------------------
WITH Dubs(ROWID, RecordID, SchemaName, TableName) AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY TableName ORDER BY Sequence DESC) as ROWID, Sequence, SchemaName, TableName
FROM @Temp2
)
DELETE FROM Dubs
WHERE ROWID > 1;
RETURN
END
CREATE FUNCTION dbo.GetTableSequence ()
RETURNS @Temp2 Table
( Sequence int Primary key Identity
, ObjectID int
, SchemaName sysname
, TableName sysname)
AS
BEGIN
DECLARE @Temp1 Table
( ForeignObjectID int
, ForeignSchemaName sysname
, ForeignTableName sysname
, ObjectID int
, SchemaName sysname
, TableName sysname)
-----------------------------------------------------
-- Identify Tables with Depedency on another table
-----------------------------------------------------
Insert into @Temp1 (ForeignObjectID, ForeignSchemaName, ForeignTableName, ObjectID, SchemaName, TableName)
Select Distinct O.Object_ID as ForeignObjectID, Cast(schema_Name (o.Schema_ID) as sysname) as ForeignSchemaName, cast(o.name as sysname) as ForeignTableName,
o2.Object_ID as ObjectID, Cast(schema_Name (o2.Schema_ID) as sysname) as SchemaName, cast(o2.Name as sysname) as TableName
From sys.foreign_keys f
Join sys.objects o
on f.referenced_object_id = o.object_id
Join sys.objects o2
on f.parent_object_id = o2.object_id
Where O.Name <> O2.Name -- Exclude Child-Parent Relationships (These causes infinite loops)
---------------------------------------------------------------------------------------
-- Recursively Identify Tables from Most Dependent to least Dependent
---------------------------------------------------------------------------------------
;
with RecursionCTE (ObjectId, SchemaName, TableName)
as
(-- First Get all tables that are not dependent on any other table. (Table can depend on them but not the other way a round)
SELECT T.ObjectID, T.SchemaName, T.TableName
FROM (Select Distinct O.Object_ID as ObjectID
, Cast(schema_Name (o.Schema_ID) as sysname) as SchemaName
, Cast(o.name as sysname) as TableName
From sys.objects o
Join sys.columns c
on c.object_id = o.object_id
Where o.type = 'u') as T
LEFT OUTER JOIN sys.foreign_key_columns sfk
ON sfk.referenced_object_id = T.ObjectID
Where sfk.referenced_object_id is null
union all
-- Now recursively work through all dependencies
select R1.ForeignObjectID, R1.ForeignSchemaName, R1.ForeignTableName
FROM @Temp1 as R1
join RecursionCTE as R2 on R1.TableName = R2.TableName
)
Insert Into @Temp2(ObjectId, SchemaName, TableName)
select ObjectID, SchemaName, TableName
from RecursionCTE
;
----------------------------------------------------
-- Delete Duplicates but keep the very last entry
----------------------------------------------------
WITH Dubs(ROWID, RecordID, SchemaName, TableName) AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY TableName ORDER BY Sequence DESC) as ROWID, Sequence, SchemaName, TableName
FROM @Temp2
)
DELETE FROM Dubs
WHERE ROWID > 1;
RETURN
END
Wednesday, September 12, 2012
Planning & Building Cubes: Tasks, Deliverables, Participants, & Schedules
The following list of tasks, deliverables, participants, and schedules are just templates or guidelines of what our team had to go through in order to deliver new business critical cubes or adding new dimensions to business critical cubes. These cubes where 500 Gb to 1Tb in size and came from processing 24Tb of data every day. Agile methods could work, but only when the Developers are working on our cubes, which was just 4-6 weeks out of the 6 months it takes to roll out our cubes. I know: wow, but this was how we evolved the phases to protect the business critical cubes from bad data. The rest of the tasks and deliverables come from non-development sources. Make of it as you will. Hope this gives some insight in the efforts in building large business critical cubes verses your average application and reporting development. Its the difference between building a dog house verses building a sky scraper. So much more logistics and physics involved.
Green Light Phase:
Building Phase:
Green Light Phase:
- Tasks
- Business requirement gathering
- Requirement review
- Feature costing
- Feature stack ranking
- Document sign-offs
- Deliverables
- Business requirement document
- Feature costing spreadsheet
- Participants
- Key business stakeholders
- Product Managers
- Engineering Program Managers
- Development manager/leads
- Test manager/leads
- Operations manager/leads
- Data Quality manager/leads
- Schedule
- 4 weeks
--------------------------------------------------------------------
Planning Phase:
- Tasks
- Roadmap creation & review
- Hardware planning
- Deliverables
- Roadmap
- Hardware acquisition request
- Swag schedule
- Participants
- Key business stakeholders
- Product managers
- Engineering Program Managers
- Development managers/leads
- Test managers/leads
- Operations managers/leads
- DataQuality manager/leads
- Schedule
- 2 weeks
--------------------------------------------------------------------
Design Phase:
- Tasks
- Business interviews
- Use case scenarios
- Dimensional modeling
- Volume analysis
- Hardware configuration
- Document review & sign-offs
- Deliverables
- Data contracts
- Functional specs
- Service Level Agreement (SLA) Document
- Technical specs
- Test specs
- Participants
- Product Managers
- Engineering Program Managers
- Develoment leads
- Test leads
- Operation leads
- DataQuality leads
- Schedule
- 4 weeks
Building Phase:
- Tasks
- Development
- Cube Engineering Team Deliverables
- Dimension Storehouse
- Managed dimensions
- Static dimensions
- Datamart ETL
- DataMart DB
- Cube
- Baseline agg indexes
- Monitoring & instrumentation
- Reports
- Deployment document
- Release to Operations (RTO) package
- Trouble shooting guide
- ETL Pipeline Team Deliverables
- Aggregated Data streams
- Auto discovered dimensions files
- Participants
- Product managers
- Engineering Program Managers
- Developers
- Testers
- Operations
- Schedule
- 4-6 weeks
--------------------------------------------------------------------
Testing Phase
- Tasks
- Testing
- Integration testing
- Cube Team Deliverables
- Deployment document
- Sample managed dimensions
- Troubleshooting guide
- Releast to Operation (RTO) package
- Rollout plan
- ETL Pipeline Team Deliverables
- Sample streams
- Sample auto-discovery files
- Releast to Operation (RTO) Package
- Participants
- Product managers
- Engineering Program Managers
- Testers
- Developers
- DataQuality
- Schedule
- 2-4 weeks
--------------------------------------------------------------------
Release to Operations (RTO) Phase
- Tasks
- Deployment
- Hardware build out
- Historical data load
- Configure monitoring
- Deliverables
- Fully deployed solution
- Participants
- Engineering Program Managers
- Developers
- Testers
- Operations
- Schedule
- 2-4 weeks
--------------------------------------------------------------------
Release to Customer (RTC) Phase
- Tasks
- Data Quality Certification
- User Acceptance Testing (UAT)
- Customer communications on new release
- Release to Customer (RTC) Sign-off
- Deliverables
- Certification plan & exit criteria
- UAT plan & exit criteria
- RTC Check off list
- Participants
- Key business stakeholders
- Product manager
- Engineering Program Managers
- Developers
- Testers
- Operations
- DataQuality team
- Key users
- Schedule
- 2-4 weeks
Building Critical High Volume Cubes: The Challenge
The past 4 years I've been involved in delivering mission critical
line-of-business analysis and reporting cubes that feed off high volume data.
Though I've moved on to another job, I've learned a lot and wanted to pass on
some of what I've learned and experienced to others that may stumble upon my
blog. This entry is not to provide a solution but to tell a story of the high
volume data space which is suffering from an astounding rate of growth. We
weren't the most successful team out there I know. I've heard of wonderful
stories like this one (2PB Data and 24TB Cubes with high reporting speeds), I just was not part of those stories, Sadly. But I did enjoy the work
and the people that I worked with.
One day there will be an extreme high volume cloud solution that the development community can easily use to provide reporting solutions at the speed of business, but right now it still seems that we struggle. There is Hadoop and Self-Learning Bitmaps so there is hope. I have faith that some brilliant engineers out there will come up with something that will work.
(Edit: That is coming as these technologies have really matured and of course going from batch to interactive querying with Apache Tez is very promising in deed!)
--------------------------------------------------------------------------------------------
Keeping Pace with the Speed of Business Challenge: Our business was in the need to do complex web analytics and the available tools at our disposal were: a Hadoop like grid processing and storage environment (which was already in place and managed by another team upstream from us) and SSAS Cubes (This was our team’s responsibility). Using SSAS Cubes as a reporting solution was dictated from executive management. Our business needed to have new dimensions and measures added to their cubes at the speed of business. Business usually meant within 2-4 weeks or less. These cubes also needed to be filled with at least two years historical data at a daily grain. The data we needed to process was from service logs that logged events that added up to 24 Gb a day.
Reality: We rolled out new dimensions and measures to cubes every 6 months with no historical reprocessing. This meant two years of data ws available for existing dimensions/measures and new dimensions could only have data from the point we released the new cubes into pre-production, which was a month and a half before we released the cubes to business. The month and half block of time we used for processing data, data validation, and user acceptance testing.
Reason for 6 months Delivery Cycles: We were limited by other team’s schedules. First the teams need to budget for and commit to providing the new data streams. Each team may have their own development cycle in which all teams participating in the pipeline need to be coordinated and data contracts put into place. The schedules usually resulted in 6 month development cycle.
Reason for No Historical Reprocess for New Dimensions/Measures: Reprocessing two years of historical high volume data on limited hardware was too costly and prevented critical daily ETL pipeline processing. Business considered data analysis as a cost center and always wanted more than what they were willing to pay for.
--------------------------------------------------------------------------------------------
Data Size Challenge: We needed to process data sets adding up to 24 Tb data a day within 24 hours after log collection has been completed for the day and store at least two years of pre-aggregated data.
Reality: We used a Hadoop like processing and storage grid that processed the data into pre-aggregated data. We were able to store two years of data in the storage grid. The aggregated data sets that feed each data mart and cube was 10-15 Gb a day. These cubes where 500 Gb to 1 Tb in size with two years of historical data at a daily grain. It usually took 36 hours to process the data through the ETL pipeline and into the cubes after the close of daily logs.
Reason for 36 hour latency: It took 24 hours to enrich the logs which included identifying user uniqueness without exposing user identification downstream, fraud detection, and sessionize the data. It took another 12 hours to join and merge other external datasets, dimensionalize, aggregate the data, and load the data into the cubes.
--------------------------------------------------------------------------------------------
Tuning Challenge: Business expected that all cube queries should return with results within 2 seconds for 100 concurrent users in order to satisfy business reporting and business troubleshooting analysis usually done at the last minute at the end of the month. You know, the instant information at your fingertips kind of expectation.
Reality: Our average query ranged from 1 second to 5 minutes. Aggregation Indexes where needed to be added for key dimension combinations used by business for common reports. Any queries going passed 5 minutes where automatically cancelled. We did this because any query taking a lot of time caused other users to not get their simple query results until rogue query was completed. Cubes didn't handle concurrent users very well. Caching critical monthly/weekly/daily canned reports was the most reliable approach to receiving quick results. This reduced the overall users on the cubes at any given moment allowing deeper analysis to be done by key people with a stronger understanding of cube technology. This played in our favor as this group of people were so much more appreciative and understanding of what magical things we did for them. All other people got canned reports :)
--------------------------------------------------------------------------------------------
Data Quality Challenge: Business made business critical decisions based on our data from the cubes. So the data must be the highest of quality and must compare consistently and accurately with other related data available internally or externally to the business.
Reality: ETL Pipelines are living organisms. Different teams release coding changes weekly into production. Though everyone understands that protecting and managing data contracts between teams was key to the pipeline’s predictability and stability, there is always something that happens that affected the quality of the data.
A data quality team was always validating new cube releases as well as validating the data weekly for any anomalies. This would result in putting into place new data validation check automations.
The most horrible bugs were bugs in the instrumentation which can significantly impact the quality of the data logs themselves. Double logging, no logging, or recording the wrong value was the most common bugs. The bad instrumentation may not be detected right away if the data verification checks weren't good enough to detect the anomaly. So it could be weeks if not months before someone notices the problem. The solution is to fix the Instrumentation; unfortunately you can't fix the data logs (Or I should say, you shouldn't as the logs are your empirical data source). So adjustments are made downstream in the reporting systems to account for the bug.
ETL Pipeline code bugs can also go undetected, but once found can be fixed by reprocessing the data most of the time. Unfortunately if we found the problem two months into the anomaly, the business may have to determine if the benefits out way the cost to reprocess the data verses just doing an adjustment to the reporting system downstream.
Adjustments can be done to fix the reports, but an adjustment tracking system should be built to track and apply the adjustments in a predictable way so that analysts can see what adjustments were made and for what reasons on any report the view.
--------------------------------------------------------------------------------------------
Cube Hardware Challenge: We needed to have hardware available that can process and store the data, load balance the cubes and reporting services, and allow for the old and new cubes to exist in production in parallel for one to two month period of time during releases.
Reality: Hardware in relationship to human resources is cheap, but that fact of the matter is that operations team (Those that management and maintain the production environments) think very differently then the engineering teams that are focused on a product or service. The Operation Team may have thousands of servers from various groups they are managing and have been mandated to increase the efficiency of usage of servers as many have storage and processing power that go unused. We didn't have a cloud solution available that enabled easier sharing, scaling, and managing of hardware and storage. So we had to justify new hardware and storage which was like pulling teeth. Sometimes we were required to reconfigure our production environments and move cubes to share with other cubes hardware to get the hardware we needed. And to top it all off, we needed to have our hardware ordered 9 months before we needed it and it needed to be done 3 months before the beginning of the fiscal year. The real challenge was how much hardware and storage we will need for the next 9-12 months before we even know what our next delivery will require. So we had to provide on educated guess. That was hard and annoying, but we survived.
One other point about standards with the Operations team. Our Operations Team required to use a set of standard hardware from a excepted list they provided. This means specialized hardware to handle large scale cubes where out of the question. So this limitation also impacted our design and cube performance.
--------------------------------------------------------------------------------------------
Unique User Count Challenge: Web analytics community has moved on from page view, clicks, and click-throughs to using much more in-depth analysis based on Distinct Users. We needed to provide Unique User counts based on any number of dimension combinations.
Reality: As you should know distinct counts of anything are not summable. Example: I cannot add yesterday’s distinct user counts with today's distinct user counts and get a correct answer. A recalculation of distinct user counts across the two todays is required. Our number of distinct users per day visiting the website went into the hundred millions. Producing distinct user counts in the cube was not even possible with the dimensionality we required and get any reasonable performance. The result would just render the cube unusable. So we used our Hadoop like grid process environment to preprocess the distinct user counts. This was easy enough but this meant we had to pre-know exactly what combination of dimensions and the grain each report required. This limited the reporting to select combinations of dimensions and if business needed another combination not currently supported, a development cycle was required. And the Distinct User Count Reports were not in the Cube, but in a separate reporting system. We could have used drill through, but this was not a very user friendly option.
All of these constraints and challenges really caused a lot of frustration and problems for business to keep up with the speed of business. But on the positive side, the business learned to ask for exactly what the needed rather then large list of wants.
One day there will be an extreme high volume cloud solution that the development community can easily use to provide reporting solutions at the speed of business, but right now it still seems that we struggle. There is Hadoop and Self-Learning Bitmaps so there is hope. I have faith that some brilliant engineers out there will come up with something that will work.
(Edit: That is coming as these technologies have really matured and of course going from batch to interactive querying with Apache Tez is very promising in deed!)
--------------------------------------------------------------------------------------------
Keeping Pace with the Speed of Business Challenge: Our business was in the need to do complex web analytics and the available tools at our disposal were: a Hadoop like grid processing and storage environment (which was already in place and managed by another team upstream from us) and SSAS Cubes (This was our team’s responsibility). Using SSAS Cubes as a reporting solution was dictated from executive management. Our business needed to have new dimensions and measures added to their cubes at the speed of business. Business usually meant within 2-4 weeks or less. These cubes also needed to be filled with at least two years historical data at a daily grain. The data we needed to process was from service logs that logged events that added up to 24 Gb a day.
Reality: We rolled out new dimensions and measures to cubes every 6 months with no historical reprocessing. This meant two years of data ws available for existing dimensions/measures and new dimensions could only have data from the point we released the new cubes into pre-production, which was a month and a half before we released the cubes to business. The month and half block of time we used for processing data, data validation, and user acceptance testing.
Reason for 6 months Delivery Cycles: We were limited by other team’s schedules. First the teams need to budget for and commit to providing the new data streams. Each team may have their own development cycle in which all teams participating in the pipeline need to be coordinated and data contracts put into place. The schedules usually resulted in 6 month development cycle.
Reason for No Historical Reprocess for New Dimensions/Measures: Reprocessing two years of historical high volume data on limited hardware was too costly and prevented critical daily ETL pipeline processing. Business considered data analysis as a cost center and always wanted more than what they were willing to pay for.
--------------------------------------------------------------------------------------------
Data Size Challenge: We needed to process data sets adding up to 24 Tb data a day within 24 hours after log collection has been completed for the day and store at least two years of pre-aggregated data.
Reality: We used a Hadoop like processing and storage grid that processed the data into pre-aggregated data. We were able to store two years of data in the storage grid. The aggregated data sets that feed each data mart and cube was 10-15 Gb a day. These cubes where 500 Gb to 1 Tb in size with two years of historical data at a daily grain. It usually took 36 hours to process the data through the ETL pipeline and into the cubes after the close of daily logs.
Reason for 36 hour latency: It took 24 hours to enrich the logs which included identifying user uniqueness without exposing user identification downstream, fraud detection, and sessionize the data. It took another 12 hours to join and merge other external datasets, dimensionalize, aggregate the data, and load the data into the cubes.
--------------------------------------------------------------------------------------------
Tuning Challenge: Business expected that all cube queries should return with results within 2 seconds for 100 concurrent users in order to satisfy business reporting and business troubleshooting analysis usually done at the last minute at the end of the month. You know, the instant information at your fingertips kind of expectation.
Reality: Our average query ranged from 1 second to 5 minutes. Aggregation Indexes where needed to be added for key dimension combinations used by business for common reports. Any queries going passed 5 minutes where automatically cancelled. We did this because any query taking a lot of time caused other users to not get their simple query results until rogue query was completed. Cubes didn't handle concurrent users very well. Caching critical monthly/weekly/daily canned reports was the most reliable approach to receiving quick results. This reduced the overall users on the cubes at any given moment allowing deeper analysis to be done by key people with a stronger understanding of cube technology. This played in our favor as this group of people were so much more appreciative and understanding of what magical things we did for them. All other people got canned reports :)
--------------------------------------------------------------------------------------------
Data Quality Challenge: Business made business critical decisions based on our data from the cubes. So the data must be the highest of quality and must compare consistently and accurately with other related data available internally or externally to the business.
Reality: ETL Pipelines are living organisms. Different teams release coding changes weekly into production. Though everyone understands that protecting and managing data contracts between teams was key to the pipeline’s predictability and stability, there is always something that happens that affected the quality of the data.
A data quality team was always validating new cube releases as well as validating the data weekly for any anomalies. This would result in putting into place new data validation check automations.
The most horrible bugs were bugs in the instrumentation which can significantly impact the quality of the data logs themselves. Double logging, no logging, or recording the wrong value was the most common bugs. The bad instrumentation may not be detected right away if the data verification checks weren't good enough to detect the anomaly. So it could be weeks if not months before someone notices the problem. The solution is to fix the Instrumentation; unfortunately you can't fix the data logs (Or I should say, you shouldn't as the logs are your empirical data source). So adjustments are made downstream in the reporting systems to account for the bug.
ETL Pipeline code bugs can also go undetected, but once found can be fixed by reprocessing the data most of the time. Unfortunately if we found the problem two months into the anomaly, the business may have to determine if the benefits out way the cost to reprocess the data verses just doing an adjustment to the reporting system downstream.
Adjustments can be done to fix the reports, but an adjustment tracking system should be built to track and apply the adjustments in a predictable way so that analysts can see what adjustments were made and for what reasons on any report the view.
--------------------------------------------------------------------------------------------
Cube Hardware Challenge: We needed to have hardware available that can process and store the data, load balance the cubes and reporting services, and allow for the old and new cubes to exist in production in parallel for one to two month period of time during releases.
Reality: Hardware in relationship to human resources is cheap, but that fact of the matter is that operations team (Those that management and maintain the production environments) think very differently then the engineering teams that are focused on a product or service. The Operation Team may have thousands of servers from various groups they are managing and have been mandated to increase the efficiency of usage of servers as many have storage and processing power that go unused. We didn't have a cloud solution available that enabled easier sharing, scaling, and managing of hardware and storage. So we had to justify new hardware and storage which was like pulling teeth. Sometimes we were required to reconfigure our production environments and move cubes to share with other cubes hardware to get the hardware we needed. And to top it all off, we needed to have our hardware ordered 9 months before we needed it and it needed to be done 3 months before the beginning of the fiscal year. The real challenge was how much hardware and storage we will need for the next 9-12 months before we even know what our next delivery will require. So we had to provide on educated guess. That was hard and annoying, but we survived.
One other point about standards with the Operations team. Our Operations Team required to use a set of standard hardware from a excepted list they provided. This means specialized hardware to handle large scale cubes where out of the question. So this limitation also impacted our design and cube performance.
--------------------------------------------------------------------------------------------
Unique User Count Challenge: Web analytics community has moved on from page view, clicks, and click-throughs to using much more in-depth analysis based on Distinct Users. We needed to provide Unique User counts based on any number of dimension combinations.
Reality: As you should know distinct counts of anything are not summable. Example: I cannot add yesterday’s distinct user counts with today's distinct user counts and get a correct answer. A recalculation of distinct user counts across the two todays is required. Our number of distinct users per day visiting the website went into the hundred millions. Producing distinct user counts in the cube was not even possible with the dimensionality we required and get any reasonable performance. The result would just render the cube unusable. So we used our Hadoop like grid process environment to preprocess the distinct user counts. This was easy enough but this meant we had to pre-know exactly what combination of dimensions and the grain each report required. This limited the reporting to select combinations of dimensions and if business needed another combination not currently supported, a development cycle was required. And the Distinct User Count Reports were not in the Cube, but in a separate reporting system. We could have used drill through, but this was not a very user friendly option.
All of these constraints and challenges really caused a lot of frustration and problems for business to keep up with the speed of business. But on the positive side, the business learned to ask for exactly what the needed rather then large list of wants.
Tuesday, November 25, 2008
How To: Naturalize (Flatten) Child/Parent Hierarchies for use as Dimensions
I keep getting the need to quickly Naturalize hierachies for analysis. So here is a sample script that accomplishes this:
set nocount on
declare @Temp1 table
(
RecordID int Primary key NOT NULL ,
ParentRecordID int,
Description nvarchar(1000)
)
/* Start loading of test data */
insert into @Temp1 values(1,null,'CEO')
insert into @Temp1 values(2,1,'VP Marketing')
insert into @Temp1 values(3,1,'VP Operations')
insert into @Temp1 values(4,2,'Marketing Director - Direct Mail')
insert into @Temp1 values(5,2,'Marketing Director - TV')
insert into @Temp1 values(6,1,'VP Research')
insert into @Temp1 values(7,4,'Human Resources Director')
insert into @Temp1 values(8,4,'Program Manager')
insert into @Temp1 values(9,6,'Research Analyst')
set nocount off;
with RecursionCTE (RecordID,ParentRecordID, Description, TOC, L1ID, Level1, L2ID, Level2, L3ID, Level3, L4ID, Level4, LevelCount)
as
(
select RecordID,ParentRecordID, Description, convert(varchar(1000),Description) TOC, RecordID as L1ID, Description as Level1, CONVERT(INT, NULL) as L2ID, Convert(nvarchar(1000), '') as Level2, CONVERT(INT, NULL) as L3ID, Convert(nvarchar(1000), '') as Level3, CONVERT(INT, NULL) as L4ID, Convert(nvarchar(1000), '') as Level4, Convert(int, 1) as LevelCount
from @Temp1
where ParentRecordID is null
union all
select R1.RecordID,
R1.ParentRecordID,
R1.Description,
case when DataLength(R2.TOC) > 0
then convert(varchar(1000),R2.TOC + '->'
+ cast(R1.Description as varchar(100)))
else convert(varchar(1000),
cast(R1.Description as varchar(100)))
end as TOC,
L1ID = R2.L1ID,
Level1 = R2.Level1,
L2ID = convert(INT, CASE WHEN R2.LevelCount = 1 THEN R1.RecordID ELSE R2.L2ID END),
Level2 = convert(nvarchar(1000), CASE WHEN R2.LevelCount = 1 THEN R1.Description ELSE R2.LEVEL2 END),
L3ID = convert(INT, CASE WHEN R2.LevelCount = 2 THEN R1.RecordID ELSE R2.L3ID END),
Level3 = convert(nvarchar(1000), CASE WHEN R2.LevelCount = 2 THEN R1.Description ELSE R2.LEVEL3 END),
L4ID = convert(INT, CASE WHEN R2.LevelCount = 3 THEN R1.RecordID ELSE R2.L4ID END),
Level4 = convert(nvarchar(1000), CASE WHEN R2.LevelCount = 3 THEN R1.Description ELSE R2.LEVEL4 END),
LevelCount = R2.LevelCount + 1
from @Temp1 as R1
join RecursionCTE as R2 on R1.ParentRecordID = R2.RecordID
)
select * from RecursionCTE
set nocount on
declare @Temp1 table
(
RecordID int Primary key NOT NULL ,
ParentRecordID int,
Description nvarchar(1000)
)
/* Start loading of test data */
insert into @Temp1 values(1,null,'CEO')
insert into @Temp1 values(2,1,'VP Marketing')
insert into @Temp1 values(3,1,'VP Operations')
insert into @Temp1 values(4,2,'Marketing Director - Direct Mail')
insert into @Temp1 values(5,2,'Marketing Director - TV')
insert into @Temp1 values(6,1,'VP Research')
insert into @Temp1 values(7,4,'Human Resources Director')
insert into @Temp1 values(8,4,'Program Manager')
insert into @Temp1 values(9,6,'Research Analyst')
set nocount off;
with RecursionCTE (RecordID,ParentRecordID, Description, TOC, L1ID, Level1, L2ID, Level2, L3ID, Level3, L4ID, Level4, LevelCount)
as
(
select RecordID,ParentRecordID, Description, convert(varchar(1000),Description) TOC, RecordID as L1ID, Description as Level1, CONVERT(INT, NULL) as L2ID, Convert(nvarchar(1000), '') as Level2, CONVERT(INT, NULL) as L3ID, Convert(nvarchar(1000), '') as Level3, CONVERT(INT, NULL) as L4ID, Convert(nvarchar(1000), '') as Level4, Convert(int, 1) as LevelCount
from @Temp1
where ParentRecordID is null
union all
select R1.RecordID,
R1.ParentRecordID,
R1.Description,
case when DataLength(R2.TOC) > 0
then convert(varchar(1000),R2.TOC + '->'
+ cast(R1.Description as varchar(100)))
else convert(varchar(1000),
cast(R1.Description as varchar(100)))
end as TOC,
L1ID = R2.L1ID,
Level1 = R2.Level1,
L2ID = convert(INT, CASE WHEN R2.LevelCount = 1 THEN R1.RecordID ELSE R2.L2ID END),
Level2 = convert(nvarchar(1000), CASE WHEN R2.LevelCount = 1 THEN R1.Description ELSE R2.LEVEL2 END),
L3ID = convert(INT, CASE WHEN R2.LevelCount = 2 THEN R1.RecordID ELSE R2.L3ID END),
Level3 = convert(nvarchar(1000), CASE WHEN R2.LevelCount = 2 THEN R1.Description ELSE R2.LEVEL3 END),
L4ID = convert(INT, CASE WHEN R2.LevelCount = 3 THEN R1.RecordID ELSE R2.L4ID END),
Level4 = convert(nvarchar(1000), CASE WHEN R2.LevelCount = 3 THEN R1.Description ELSE R2.LEVEL4 END),
LevelCount = R2.LevelCount + 1
from @Temp1 as R1
join RecursionCTE as R2 on R1.ParentRecordID = R2.RecordID
)
select * from RecursionCTE
Monday, December 10, 2007
How To: Delete Duplicate Data (DeDup)
ETL processes typically will run across the need to delete duplicate records. The fastest and simplist way I know of is to create a Primary Key (or unique index) on the table with the instruction to IGNORE_DUP_KEY=ON . Then when you bulk copy or insert data into the table, SQL quickly ingores the duplicates.
Example:
CREATE TABLE #Temp (ID INT NOT NULL)
ALTER TABLE #Temp
ADD PRIMARY KEY (ID ASC)
WITH (IGNORE_DUP_KEY = ON)
GO
INSERT INTO #Temp (ID)
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 1
SELECT * FROM #Temp
DROP TABLE #Temp
But sometimes you need to delete duplicate data in a more controlled and auditable way. Here is an approach that does it in a single set statement.
Example:
CREATE TABLE #Temp(ID INT, Name VARCHAR(255))
INSERT INTO #Temp (ID, Name)
SELECT 1, 'Name 1'
UNION ALL
SELECT 2, 'Name 2'
UNION ALL
SELECT 3, 'Name 3'
UNION ALL
SELECT 4, 'Name 4'
UNION ALL
SELECT 5, 'Name 2'
UNION ALL
SELECT 6, 'Name 1'
UNION ALL
SELECT 7, 'Name 3'
UNION ALL
SELECT 8, 'Name 5'
UNION ALL
SELECT 9, 'Name 4'
UNION ALL
SELECT 10, 'Name 4'
SELECT 'Deleted Data Set'
DELETE FROM #TEMP
OUTPUT DELETED.* -- Display the Deleted Rows
FROM #TEMP T
JOIN (SELECT ROW_NUMBER() OVER (PARTITION BY #TEMP.Name ORDER BY #TEMP.ID) as ROWID, #TEMP.*
FROM #TEMP
) AS DupRows -- Find Dubs and assign rowID which resets for each new name
ON DupRows.ID = T.ID
WHERE DupRows.ROWID > 1
-- OR Here is an even more eligant solution --
;
WITH Dubs(ROWID, ID, Name) AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY #TEMP.Name ORDER BY #TEMP.ID) as ROWID, #TEMP.*
FROM #TEMP
)
DELETE FROM Dubs
OUTPUT DELETED.* -- Display the Deleted Rows
WHERE ROWID > 1 ;
SELECT 'Clean Data Set'
SELECT * FROM #TEMP
DROP TABLE #TEMP
GO
Example:
CREATE TABLE #Temp (ID INT NOT NULL)
ALTER TABLE #Temp
ADD PRIMARY KEY (ID ASC)
WITH (IGNORE_DUP_KEY = ON)
GO
INSERT INTO #Temp (ID)
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 1
SELECT * FROM #Temp
DROP TABLE #Temp
But sometimes you need to delete duplicate data in a more controlled and auditable way. Here is an approach that does it in a single set statement.
Example:
CREATE TABLE #Temp(ID INT, Name VARCHAR(255))
INSERT INTO #Temp (ID, Name)
SELECT 1, 'Name 1'
UNION ALL
SELECT 2, 'Name 2'
UNION ALL
SELECT 3, 'Name 3'
UNION ALL
SELECT 4, 'Name 4'
UNION ALL
SELECT 5, 'Name 2'
UNION ALL
SELECT 6, 'Name 1'
UNION ALL
SELECT 7, 'Name 3'
UNION ALL
SELECT 8, 'Name 5'
UNION ALL
SELECT 9, 'Name 4'
UNION ALL
SELECT 10, 'Name 4'
SELECT 'Deleted Data Set'
DELETE FROM #TEMP
OUTPUT DELETED.* -- Display the Deleted Rows
FROM #TEMP T
JOIN (SELECT ROW_NUMBER() OVER (PARTITION BY #TEMP.Name ORDER BY #TEMP.ID) as ROWID, #TEMP.*
FROM #TEMP
) AS DupRows -- Find Dubs and assign rowID which resets for each new name
ON DupRows.ID = T.ID
WHERE DupRows.ROWID > 1
-- OR Here is an even more eligant solution --
;
WITH Dubs(ROWID, ID, Name) AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY #TEMP.Name ORDER BY #TEMP.ID) as ROWID, #TEMP.*
FROM #TEMP
)
DELETE FROM Dubs
OUTPUT DELETED.* -- Display the Deleted Rows
WHERE ROWID > 1 ;
SELECT 'Clean Data Set'
SELECT * FROM #TEMP
DROP TABLE #TEMP
GO
Monday, November 05, 2007
Free Code: Stored Procedure Code Generator
The above link provides the following:
1. DBCodeGeneration.zip
Description:
You will find that 80% of all stored procedures in a transaction database are all very predictable and can be auto generated. Using a code generator will save you time and create symmetry within your database which increases predictability and stability for the overall system. This will allow you to focus on the other 20% of the stored procedures which are more complex and critical to the system.
I have found that creating stored procedures that create stored procedures was an easy way to do code generation for your database. Creating a script that depended on Visual Studio is just too bulky and slow to load when you want quick results. This solution I can take with me any were without the need of a complex environment.
Feature List:
1. Creates Get Stored Procedures
2. Creates Set Stored Procedures (Performs Insert or Update process depending on conditions)
3. Creates Delete Stored Procedures
Wednesday, August 01, 2007
How To: Simple Query-able Compression (No need to decompress to read file)
Do you wish to compress your data without having to decompress it to read it? Most people will think of RAR or ZIP compression when they need to save space on the hard drive. This may save storage and I/O load, but the side effect of this approach will require you to decompress the file every time you need to access it. The following is a means to compress your data without requiring decompression to read it.
Normalization: Compression is a natural byproduct when normalizing your data (Please see articles below on normalization and modeling). By normalizing your data you remove redundant data. It’s an effective non-destructive means of compressing your data into a query enabled format.
Binary Conversion: Converting from a string formatted file into a binary formatted file is another natural means of compressing your data. Reducing a string value of “1002000032” in to 4 bytes saves 6 bytes. The strongly data typed binary file can be trusted and read by other business processes without the need to do string conversion.
Hashing Long String Values: Hashing long string values into a binary hash value and placing the string and corresponding hash value into a lookup table is another natural means of compressing your data. URL links are common storage hogs. Reducing a 255 byte URL string to a 64 bit hash can save lots of space if that URL string occurs multiple times within the file. (NOTE: Make sure you select the most appropriate Hashing algorithm and the right hash bit length to reduce your odds of collisions.)
Roll Ups (Aggregation): By only recording one unique row and placing an aggregation count for each time it was recorded within a unit of time you can reduce the amount of data being recorded(Example: John Doe hit your website home page 3 times in 1 hr. In the log there would be one record with an aggregate count value of 3). This is destructive to your data set, because you lose the retreading of a user’s event path. But this may be a minimal and acceptable loss of data depending on your business.
Or Get Up To 36x Compression
The typical compression results from using one or more of the above suggestions can result in 2x-6x compression ratio. The above suggestions are extremely valuable even if you don’t care about having query enabled compression. If you add RAR on top of it all you can save another 6x compression which can give you between 12x-36x compression. Not bad for saving space eh!
Normalization: Compression is a natural byproduct when normalizing your data (Please see articles below on normalization and modeling). By normalizing your data you remove redundant data. It’s an effective non-destructive means of compressing your data into a query enabled format.
Binary Conversion: Converting from a string formatted file into a binary formatted file is another natural means of compressing your data. Reducing a string value of “1002000032” in to 4 bytes saves 6 bytes. The strongly data typed binary file can be trusted and read by other business processes without the need to do string conversion.
Hashing Long String Values: Hashing long string values into a binary hash value and placing the string and corresponding hash value into a lookup table is another natural means of compressing your data. URL links are common storage hogs. Reducing a 255 byte URL string to a 64 bit hash can save lots of space if that URL string occurs multiple times within the file. (NOTE: Make sure you select the most appropriate Hashing algorithm and the right hash bit length to reduce your odds of collisions.)
Roll Ups (Aggregation): By only recording one unique row and placing an aggregation count for each time it was recorded within a unit of time you can reduce the amount of data being recorded(Example: John Doe hit your website home page 3 times in 1 hr. In the log there would be one record with an aggregate count value of 3). This is destructive to your data set, because you lose the retreading of a user’s event path. But this may be a minimal and acceptable loss of data depending on your business.
Or Get Up To 36x Compression
The typical compression results from using one or more of the above suggestions can result in 2x-6x compression ratio. The above suggestions are extremely valuable even if you don’t care about having query enabled compression. If you add RAR on top of it all you can save another 6x compression which can give you between 12x-36x compression. Not bad for saving space eh!
Subscribe to:
Posts (Atom)


