【问题标题】:Query Transposing certain rows into column names查询将某些行转换为列名
【发布时间】:2009-05-29 14:16:15
【问题描述】:

我有几个看起来像这样的表 表一

user_id   |  name
-------------------------   
x111      |   Smith, James
x112      |   Smith, Jane

等等。

表 2

id    |   code    |    date       |  incident_code    | user_id
-----------------------------------------------------------------
1     |    102008 |   10/20/2008  |    1              | x111
2     |    113008 |   11/30/2008  |    3              | x111
3     |    102008 |   10/20/2008  |    2              | x112
4     |    113008 |   11/30/2008  |    5              | x112 

我想展示的是这样的

user_id     |    user_name    |   INCIDENT IN OCT 2008   | INCIDENT IN NOV 2008
------------------------------------------------------------------------------ 
x111        |    Smith, John  |   1                      | 3
x112        |    Smith, Jane  |   2                      | 5

等等。

event_code 将替换为位于另一个表中的事件的实际描述,但我想我会先看看它是如何工作的。

有些列标题是静态的,而另一些则是根据日期创建的。 有谁知道我如何使用 sql server 2005 做到这一点?一些例子会很有帮助。

提前致谢

【问题讨论】:

    标签: sql sql-server pivot transpose


    【解决方案1】:

    这是一个使用 PIVOT 生成并运行动态 SQL 的解决方案:

    DECLARE @pivot_list AS VARCHAR(MAX)
    
    --
    ;
    WITH    cols
              AS ( SELECT DISTINCT
                            'INCIDENT IN ' + LEFT(UPPER(CONVERT(VARCHAR, [date], 107)),
                                                  3) + ' '
                            + SUBSTRING(UPPER(CONVERT(VARCHAR, [date], 107)), 9, 4) AS col
                   FROM     so926209_2
                 )
        SELECT  @pivot_list = COALESCE(@pivot_list + ', ', '') + '[' + col + ']'
        FROM    cols
    
    --
    DECLARE @template AS VARCHAR(MAX)
    SET @template = 'WITH incidents AS (
    SELECT  [user_id],
            incident_code,
            ''INCIDENT IN '' + LEFT(UPPER(CONVERT(VARCHAR, [date], 107)), 3)
            + '' '' + SUBSTRING(UPPER(CONVERT(VARCHAR, [date], 107)), 9, 4) AS col
    FROM    so926209_2
    )
    ,results AS (
    SELECT * FROM incidents PIVOT (MAX(incident_code) FOR col IN ({@pivot_list})) AS pvt
    )
    SELECT results.[user_id]
        ,so926209_1.[name]
        ,{@select_list}
    FROM results INNER JOIN so926209_1 ON so926209_1.[user_id] = results.[user_id]
    '
    
    DECLARE @sql AS VARCHAR(MAX)
    SET @sql = REPLACE(REPLACE(@template, '{@pivot_list}', @pivot_list), '{@select_list}', @pivot_list)
    
    --PRINT @sql
    EXEC (@sql)
    

    so926209_1,so926209_2 是你的表 1 和表 2

    请注意,如果同一个人在一个月内发生了多次事件,则您的示例不会显示您希望如何处理。此示例仅使用该月的最后一个事件。

    【讨论】:

    • 感谢凯德。更改它以使其也包含日期应该不难。每个日期不会有超过一个事件。您是否知道对于用户表中的 100 万用户和事件表中的大约 1200 万事件来说,这可能有多慢?
    • 这不会是我每秒都跑的那种事情!在我们做的一些后端处理中,我有一些相当大的 PIVOT 和 UNPIVOT 操作,我们将从几百万行和 PIVOT 增加到几十万,或者从几百万行和 UNPIVOT 到几千万,但是其中一些操作需要几分钟(8 路 32GB,SQL 2005 Enterprise)。在 PIVOT 之前构建字符串以使标题完全按照您指定的方式进行分组并不是将它们分组的最有效方法 - 我会在 int 中使用像 YYYYMM 这样的缩写,它的字节数会少得多。
    • 然后您的 {@pivot_list} 看起来会与您的 {@select_list} 有很大不同,后者的生成方式会略有不同,以包含 [200801] AS [INCIDENT IN JAN 2008] 之类的内容。 DISTINCT 也会更快。并且列顺序不会按字母顺序排列。我没有费心去做——我认为这使得通用 PIVOT 技术在代码生成中变得清晰,无论是及时生成还是提前定期生成。
    【解决方案2】:
    【解决方案3】:

    这听起来像是一项报告任务。报告,通常从数据库的角度被称为 OLAP,在线分析处理,往往与“传统”数据库访问,OLTP(在线事务处理)有很大不同,因为它通常由跨越更长时期的大量数据聚合组成的时间。很多时候,您正在寻找的那种聚合。

    使用 Tetraneutron 建议的 Pivot 对于较小的数据集就足够了。但是,随着您需要报告的数据量的增长,您可能需要更高级的东西。 OLAP 由 SQL Server Analysis Services (SSAS) 提供,于 2005 年和 2008 年可用。使用 SSAS,您可以创建多维数据存储库,这些存储库可以直接从 OLTP 数据库或从中间数据仓库数据库预聚合数据。多维数据(通常称为多维数据集)提供了一种更快的方式来访问可以从 Pivot 获取的数据类型,而不会影响 OLTP 数据库中标准事务处理的性能。

    如果您需要报告的数据多于少量,我建议您查看 SQL Server Analysis Services 2005、OLAP、Cubes 和 MDX(T-SQL 的多维扩展)。曲线来设置 OLAP 多维数据集,但是一旦设置好,如果您有大量的报告需求,拥有它的好处可能是巨大的。

    【讨论】:

      【解决方案4】:

      这样的查询会起作用:

      select
          u.User_id,
          u.Name,
          Okt2008Sum = sum(case when i.date between 
              '2008-10-01' and '2008-11-01' then 1 else 0 end),
          Nov2008Sum = sum(case when i.date between 
              '2008-11-01' and '2008-12-01'then 1 else 0 end)
      from #incidents i
      inner join #users u on i.user_id = u.user_id
      group by u.user_id, u.name
      

      根据您的客户端以及运行它的频率,您可以生成此查询。在 SQL 中,这看起来像:

      create table #months (
          MonthName varchar(25),
          StartDate datetime
      )
      
      insert into #months values ('Okt2008','2008-10-01')
      insert into #months values ('Nov2008','2008-11-01')
      
      declare @query varchar(8000)
      select @query = 'select u.User_id, u.Name '
      
      select @query = @query + ', ' + MonthName + 
          ' = sum(case when i.date between ''' + cast(StartDate as varchar) + 
          ''' and ''' + cast(dateadd(m,1,StartDate) as varchar) + 
          ''' then 1 else 0 end) '
      from #Months
      
      select @query = @query + '
          from #incidents i
          inner join #users u on i.user_id = u.user_id
          group by u.user_id, u.name'
      
      exec (@query)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多