SQL:查找表行为列以进行报告

时间:2013-12-12 16:55:52

标签: sql sql-server-2005 pivot

我有以下两个表数据结构来处理自定义用户字段:

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

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

这允许定义(全局)任意数量的字段,并允许用户具有这些自定义字段的值。我需要弄清楚如何将这些信息显示为报告目的,作为预先存在的报告的一部分,采用以下格式:

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

我知道这可能涉及使用PIVOT或UNPIVOT,但尽可能尝试,他们只会让我感到困惑。

提前致谢

1 个答案:

答案 0 :(得分:4)

有几种不同的方法可以获得结果,您可以使用带有CASE表达式的聚合函数,也可以使用PIVOT函数来获取结果。根据您的注释,可以定义任意数量的字段,听起来您需要使用动态SQL来获得最终结果。在编写动态SQL版本之前,我总是从查询的静态或硬编码版本开始,然后将其转换为动态SQL。

除了使用这些方法之外,我还建议使用窗口函数row_number()useridfieldname的每个组合生成唯一值。由于您正在转换字符串值,因此您必须使用max / min聚合函数,该函数将为每个字段名仅返回一个值,通过添加row_number,您将能够返回多个组合每个用户都有Location等。

如果您使用带有CASE表达式的聚合函数,则查询将为:

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;

请参阅SQL Fiddle with Demo

如果您使用的是PIVOT,则查询的硬编码版本为:

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;

请参阅SQL Fiddle with Demo

一旦拥有了正确的逻辑,就可以将PIVOT转换为要执行的动态SQL:

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;

SQL Fiddle with Demo。所有版本都会给出结果:

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