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