Technical Article

Did you say denormalize, why?

,

Hi everyone,   

Some time ago I was being asked if there was a way to denormalize a query result from a 1-to-many related tables/views.

    Normally, you would get multiple row results set, in most cases.  But, what was being asked was if there was a way to

    grab the "many" part and transpose those into a single line with dynamic columns along with the rest of the data from the "1" part of the

    query result.

    At first, I thought that was going to be an easy one and PIVOT popped in my mind rather quickly.  Don't get me wrong, it is

    still an easy one, but, I was surprised that I was totally misunderstood the question and few minutes later I realized

    that I should be on a different path.

    PIVOT or the Matrix layouts in Report Builder 3.0 or in MSSQL Analysis Server the "behind the scene" functions

    do provide that information to us quickly. So, why are we wasting our time on this?  That was my question too!

    This script may be helpful if one would like to pass a single parameter, wrap up and transpose the data and drop it on an

    online or real-time page or frame for viewing purposes only, then that is great. The key is "real-time" information with the minimum cost.

    

    You may want to alter this code and take it to the next level by make this more dynamic, from accepting TABLE name,

    User Name, and even a JOIN statement along with the in question COLUMN and get  your result-set dropped on screen.

    

    Hope this may be helpful and useful and enjoy

    

    JohnE

    

    ACTIONS:

     CREATE THE TEST SROUCE TABLES WITH DATA

     DISPLAY THE INSERTED DATA FOR DEBUGGING

     CREATE A STAGING TABLE AS A SOURCE TABLE

     CREATE ANOTHER STAGING TABLE AS A TARGET FOR THE DENORMALIZED DATA COLUMNS

     ALTER TARGET TABLE BY ADDING COLUMNS DYNAMICALLY

     LOOP THRU THE DENORMALIZED COUMNS AND UPDATE THE CORRESPONDING COLUMNS

-- CREATE THE TEST TABLES WITH DATA 
create table #Users(
UserID int NOT NULL IDENTITY(1000,1)
,UserName nvarchar(64) NOT NULL
)

create table #UserRelatedData(
userPerfID int NOT NULL IDENTITY(1000,1)
,UserID int NOT NULL
,RelatedID int NOT NULL
,RelatedData nvarchar(64)
)

insert into #Users(UserName) values('Samir Nagheenanajar')
insert into #Users(UserName) values('Bob1 The Consultant')
insert into #Users(UserName) values('Bob2 The Consultant')


insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 100, 'Get Interviewed')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 110, 'Get Hired')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 120, 'Remain Happy')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 130, 'Educate Self')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 130, 'Take out PCLoader')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 130, 'Fractions of a penny')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 140, 'Check Marketability')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 140, 'Jump 2 Conclusion Mat')
insert into #UserRelatedData (UserID, RelatedID, RelatedData) values(1000, 140, 'Get Rehired')

-- DISPLAY THE INSERTED DATA FOR DEBUGGING AND TRAINING PURPOSES
select * from #users
select * from #UserRelatedData
-- ===============================================================================
-- ===============================================================================
-- DECLARE THE VARIABLES FOR THE CURSOR

declare @U_UserID as int
declare @U_UserName as nvarchar(64)
declare @UP_PrefData as nvarchar(64) 
declare @RecCount int
declare @OutString as nvarchar(64)
declare @i int

-- ==============================================================
-- CREATE A STAGING TABLE AS A SOURCE TABLE
SELECT  U.UserID U_UserID, U.UserName U_UserName, UP.RelatedData UP_PrefData
into #SourceTable
FROM #Users AS U LEFT OUTER JOIN
                #UserRelatedData AS UP ON U.UserID = UP.UserID
where U.UserID = 1000
-- ===============================================================

select @RecCount = count(*) from #SourceTable 
set @OutString = '' 
set @i = 1 

-- ==========================================================================
-- CREATE ANOTHER STAGING TABLE AS A TARGET FOR THE DENORMALIZED DATA COLUMNS
create table #TargetTable  (
UserId int not null 
,UserName nvarchar(64) not null
)
-- ==========================================================================

-- ALTER TARGET TABLE BY ADDING COLUMNS DYNAMICALLY
while @i <= @RecCount
begin
set @OutString = 'alter table #TargetTable add RelatedData_' + convert(nvarchar(64),@i) +' nvarchar(64) null'
exec(@outstring) 

set @i += 1 
end

-- INSERT THE NORMALIZED DATA, SUCH AS USER ID AND THE USER NAME
insert into #TargetTable(userid , UserName) values((select top 1 u_userid from #SourceTable), (select top 1 u_username from #sourcetable))

-- LOOP THRU THE DENORMALIZED COUMNS AND UPDATE THE CORRESPONDING COLUMNS

declare MyCursor cursor for 
select * from #SourceTable 

open mycursor 

fetch next from mycursor
into @U_UserID, @U_UserName, @UP_PrefData

set @i = 1
set @outstring = ''

while @i <= @reccount  
begin
set @outstring = 'update #TargetTable set RelatedData_'+ convert(nvarchar(64), @i) + '=''' + convert(nvarchar(64), @Up_PrefData) + ''''
exec(@OutString)

fetch next from mycursor
into @U_UserID, @U_UserName, @UP_PrefData
 
set @i +=1
end

-- DISPLAY THE FINAL OUTPUT TABLE WITH DENORMALIZED COLUMNS
select * from #TargetTable

-- JUST TO MAKE SURE THEY ARE GONE..
DROP TABLE #USERS
DROP TABLE #UserRelatedData
drop table #TargetTable 
drop table #SourceTable 
close mycursor 
deallocate mycursor

Rate

3 (4)

You rated this post out of 5. Change rating

Share

Share

Rate

3 (4)

You rated this post out of 5. Change rating