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;
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.