I need to select static colums + a dynamic number of rows as columns in SQL
TABLE 1
-------
HotelID
BlockID
BlockName
TABLE 2
-------
BlockDate (unknown number
I once wrote a stored procedure that did just something like this. Given are a users table with basic details and a variable number of profile properties for users. (The number of profile properties varies per DotNetNuke Portal)
This is straight from DotNetNuke 4.9, check the database schema from there and you'll get the details. Tables involved are Users, UserPortals, UserProfile, ProfilePropertyDefinition
In short words :
This gives me one row per user with all profile properties.
Here the code - not perfect but hey its tailored for my needs but should be easy enough to re-use (tried to avoid cursors btw)
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROCEDURE [dbo].[rows2cols] @portalId INT
AS BEGIN
print 'PortalID=' + convert(varchar,@portalId)
--SET NOCOUNT ON;
declare @idx int
declare @rowcount int
declare @tmpStr nvarchar(max)
declare @ctype nvarchar(max)
declare @cname nvarchar(max)
declare @clen int
declare @createStr nvarchar(max)
---------------------------------------------------------------------------
-- create tmp table --
---------------------------------------------------------------------------
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[xxxx]') AND type in (N'U'))
DROP TABLE [dbo].[xxxx]
print 'Building Temp Table Cols for profile properties...'
set @rowcount = (select count(*) from ProfilePropertyDefinition where PortalID=0 and deleted=0)
set @idx = 1
set @tmpStr = ''
while (@idx <= @rowcount)
begin
-- dynamically generate rownumbers to be able to do loop over them (avoid cursors)
select @cname = t1.PropertyName from
( select ROW_NUMBER() OVER (ORDER BY ViewOrder) as Idx, PropertyName from ProfilePropertyDefinition
where PortalID=0 and deleted=0
) as t1 where t1.Idx = @idx
if (@cname = 'Email' or @cname = 'FirstName' or @cname = 'LastName') begin
set @clen = 1
end else begin
set @tmpStr = @tmpStr + '[' + @cname + '] [nvarchar](500), '
end
set @idx = @idx + 1
end
set @tmpStr = @tmpStr + '[userid] [int] '
set @createStr = 'create table xxxx ( ' + @tmpStr + ' )'
Print @createStr
Exec (@createStr)
---------------------------------------------------------------------------
-- fill tmp table --
---------------------------------------------------------------------------
declare @propName nvarchar(max)
declare @propVal nvarchar(max)
declare @userId int
declare @idx2 int
declare @rowcount2 int
declare @inscol nvarchar(max)
declare @insval nvarchar(max)
set @rowcount = (select count(*) FROM Users LEFT OUTER JOIN UserPortals ON Users.UserID = UserPortals.UserId WHERE UserPortals.PortalId = @portalId)
set @idx = 1
while (@idx <= @rowcount)
begin
-- get userId
select @userId = t1.UserID from (select u.UserID, ROW_NUMBER() OVER (ORDER BY u.UserID) as Idx
from Users as u LEFT OUTER JOIN UserPortals as up ON u.UserID = up.UserId where up.PortalId = @portalId) as t1
where t1.Idx = @idx
set @idx2 = 1
set @rowcount2 = (select count(*) from UserProfile where UserID = @userId)
set @inscol = ''
set @insval = ''
while (@idx2 < @rowcount2)
begin
-- build insert for a specific user
select @propName = t1.PropertyName , @propVal=t1.PropertyValue from
( select ROW_NUMBER() OVER (ORDER BY ProfileID) as Idx, up.PropertyDefinitionID,ppd.PropertyName, up.PropertyValue
from UserProfile as up
inner join ProfilePropertyDefinition as ppd on up.PropertyDefinitionID = ppd.PropertyDefinitionID
where UserID = @userId
) as t1 where t1.Idx = @idx2
if (@propName != 'Firstname' and @propName != 'LastName' and @propName != 'Email')
begin
set @inscol = @inscol + @propName + ', '
set @insval = @insval + 'N''' + replace(@propVal,'''','''''') + ''', '
end
set @idx2 = @idx2 + 1
end
set @inscol = @inscol + 'userid'
set @insval = @insval + convert(nvarchar,@userId)
set @tmpStr = 'insert into xxxx (' + @inscol + ') values (' + @insval + ')'
--print @tmpStr
Exec(@tmpStr)
set @idx = @idx + 1
end
-- -------------------------------------------------------------------------
-- return tmp table & dump --
-- -------------------------------------------------------------------------
SELECT Users.*, xxxx.* FROM xxxx INNER JOIN Users ON xxxx.userid = Users.UserID
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[xxxx]') AND type in (N'U'))
DROP TABLE [dbo].[xxxx]
END