简体   繁体   中英

SQL: Lookup table rows into columns for reporting purposes

I have the following two table data structure for dealing with custom user fields:

[UserFieldID] [UserFieldName]
-------------------------------
1             Location
2             Color

[UserID] [UserFieldID] [UserFieldValue]
----------------------------------------
1        1             Home
1        2             Orange
2        1             Office
2        2             Red

This allows any number of fields to be defined (globally) and users to have values for those custom fields. I need to figure out how to display this information for reporting purposes as part of a pre-existing report, in the following format:

UserID   ...   Location   Color
----------------------------------------------------
1              Home       Orange
2              Office     Red

I know this probably involves using either PIVOT or UNPIVOT, but try as I might, they just confuse me.

Thanks in advance

There are several different ways that you can get the result, you can use an aggregate function with a CASE expression or you can use the PIVOT function to get this. Based on your comment that any number of fields can be defined, it sounds like you will need to use dynamic SQL to get the final result. Before writing a dynamic SQL version, I would always start with a static or hard-coded version of the query, then convert it to dynamic SQL.

Besides using these methods, I would also recommend using the windowing function row_number() to generate a unique value for each combination of userid and fieldname . Since you are pivoting string values, then you have to use either the max / min aggregate function which will return only one value for each fieldname, by adding the row_number you will be able to return multiple combinations of Location , etc for each user.

If you were using an aggregate function with a CASE expression the query would be:

select 
  userid,
  max(case when userfieldname = 'Location' then userfieldvalue end) location,
  max(case when userfieldname = 'Color' then userfieldvalue end) Color
from 
(
  select v.userid,
    f.userfieldname,
    v.userfieldvalue,
    row_number() over(partition by v.userid, v.userfieldid
                      order by v.userfieldid) seq
  from userFields f
  left join userValues v
    on f.userfieldId = v.userFieldId
) d
group by userid, seq
order by userid;

See SQL Fiddle with Demo

If you were using PIVOT, the hard-coded version of the query would be:

select userid, Location, Color
from
(
  select v.userid,
    f.userfieldname,
    v.userfieldvalue,
    row_number() over(partition by v.userid, v.userfieldid
                      order by v.userfieldid) seq
  from userFields f
  left join userValues v
    on f.userfieldId = v.userFieldId
) d
pivot
(
  max(userfieldvalue)
  for userfieldname in (Location, Color)
) p
order by userid;

See SQL Fiddle with Demo .

Once you have the correct logic you can convert the PIVOT to dynamic SQL to be executed:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(UserFieldName) 
                    from UserFields
                    group by UserFieldName, userfieldId
                    order by userfieldid
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT userid, ' + @cols + ' 
            from 
            (
              select v.userid,
                f.userfieldname,
                v.userfieldvalue,
                row_number() over(partition by v.userid, v.userfieldid
                                  order by v.userfieldid) seq
              from userFields f
              left join userValues v
                on f.userfieldId = v.userFieldId
            ) x
            pivot 
            (
                max(userfieldvalue)
                for userfieldname in (' + @cols + ')
            ) p 
            order by userid'

execute sp_executesql @query;

See SQL Fiddle with Demo . All versions will give a result:

| USERID | LOCATION |  COLOR |
|--------|----------|--------|
|      1 |     Home | Orange |
|      1 |   Office | (null) |
|      2 |   Office |    Red |

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM