2

I am trying to join three tables in SQL Server 2008 R2, where I want the items in the second table to be added as new column.

To explain in detail - I have 3 tables:

First table contains User Name and User ID

UserID UserName
1       Mike
2       John
3       George

Second Table is position ID's with Position Names

PositionID PositionName
1          RW
2          LW
3          DF
4          MDF
5          SS
6          CF
etc

Third table table contains their preferred positions where one user can have more than one

UserID  PositionId
1        1
1        3
2        2
2        3
2        5
3        2
3        7

When I join these tables I want to get single row for every user with all the preferred positions like

UserID   UserName  PreferedPosition  PreferedPosition2 PreferedPosition3
1        Mike      RW                LW               
2        John      CMF               SS                CF
3        George    LW                MDF

I don't know how to achieve this, any help would be appreciated.

Bridge
  • 29,818
  • 9
  • 60
  • 82
Burak Gazi
  • 535
  • 2
  • 7
  • 23

1 Answers1

2

If you have only a few numbers of positions, you can do it with PIVOT keyword

select
    UserID,
    UserName,
    [1] as Position1,
    [2] as Position2,
    [3] as Position3
from
(
    select
        U.UserID, U.UserName, P.PositionName,
        row_number() over (partition by U.UserID order by P.PositionName) as RowNum
    from Positions_Users as PU
        inner join Positions as P on P.PositionID = PU.PositionID
        inner join Users as U on U.UserID = PU.UserID
) as P
    pivot
    (
        min(P.PositionName)
        for P.RowNum in ([1], [2], [3])
    ) as PIV

SQL FIDDLE

If, however, you want to have a dynamic number of columns, you have to use dynamic SQL, like this

declare @stmt nvarchar(max), @stmt_columns1 nvarchar(max), @stmt_columns2 nvarchar(max)
declare @Temp_Data table (RowNum nvarchar(max))

insert into @Temp_Data
select distinct row_number() over (partition by U.UserID order by P.PositionName) as RowNum
from Positions_Users as PU
    inner join Positions as P on P.PositionID = PU.PositionID
    inner join Users as U on U.UserID = PU.UserID

select @stmt_columns1 = stuff((select ', [' + RowNum + ']' from @Temp_Data for xml path(''), type).value('.', 'nvarchar(max)'), 1, 2, '')
select @stmt_columns2 = stuff((select ', [' + RowNum + '] as Position' + RowNum from @Temp_Data for xml path(''), type).value('.', 'nvarchar(max)'), 1, 2, '')

select @stmt = '
select
    UserID,
    UserName,' + @stmt_columns2 + '
from
(
    select
        U.UserID, U.UserName, P.PositionName,
        row_number() over (partition by U.UserID order by P.PositionName) as RowNum
    from Positions_Users as PU
        inner join Positions as P on P.PositionID = PU.PositionID
        inner join Users as U on U.UserID = PU.UserID
) as P
    pivot
    (
        min(P.PositionName)
        for P.RowNum in (' + @stmt_columns1 + ')
    ) as PIV'

exec sp_executesql @stmt = @stmt

SQL FIDDLE

Roman Pekar
  • 107,110
  • 28
  • 195
  • 197
  • 1
    You could prepare the final column names by concatenating `'Position'` with the row numbers in the subselect, something like this: `'Position' + rtrim(row_number() over ...) as ...` instead of just `row_number() ... as ...`. That way you'd only need to generate one column list (`Position1, Position2...`) and use it twice (in the PIVOT and in the main SELECT). – Andriy M Dec 03 '12 at 16:34
  • @AndriyM good point, if it was production code, I'd definitely rewrite it that way – Roman Pekar Dec 03 '12 at 17:38