【问题标题】:SQL: Lookup table rows into columns for reporting purposesSQL:将表格行查找到列中以进行报告
【发布时间】:2013-12-31 05:10:59
【问题描述】:

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

[用户字段 ID] [用户字段名称] ------------------------------------------- 1 个地点 2 颜色 [用户 ID] [用户字段 ID] [用户字段值] -------------------------------------- 1 1 主页 1 2 橙色 2 1 办公室 2 2 红色

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

UserID ... 位置颜色 -------------------------------------------------- -- 1 家橙 2 办公室红

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

提前致谢

【问题讨论】:

    标签: sql sql-server-2005 pivot


    【解决方案1】:

    有几种不同的方法可以获得结果,可以使用带有 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 |
    

    【讨论】:

    • 非常感谢。这就像一种魅力,也帮助我理解了。
    • @GarethD 但是变量赋值方法是不可预测和不确定的 - 赋值顺序不能保证,所以你可以在执行之间得到不同的输出,我相信在某些情况下你可能会错过行, 也。只要您有 ORDER BY,FOR XML PATH 就具有确定性。此外,仅通过查看查询成本的百分比来比较两个查询是一个笑话。 CPU、读取和持续时间是价值数千倍的指标。无论如何,当比较一个有效的查询和一个可能有效的查询时,我不在乎哪个更快,我首先重视正确。
    • 我非常正确。我一直使用 XML PATH 方法,但我最近遇到了一个实例,其中更改为使用变量赋值方法并且它不断提高性能,所以深入挖掘,正如已经提到的,执行计划几乎没有给出关于XML 方法,由于 IO 是相同的,并且对于 XML 方法的 CPU 成本更高,我只能假设查询的额外成本来自 XML 读取器表值函数。我生活,我学习,希望我不会停止学习。
    猜你喜欢
    • 2018-10-12
    • 2017-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2013-01-08
    • 1970-01-01
    • 2022-01-17
    相关资源
    最近更新 更多