【问题标题】:Pivoting on two comma delimited columns透视两个逗号分隔的列
【发布时间】:2012-10-17 04:52:53
【问题描述】:

我正在努力解决这个问题,我在一个表中有两个字段,一个包含标题名称,另一个包含结果

例如,

ResultID  TestID   MemberID    HeaderDefinition             ResultDefinition
1         1        1           Minutes Exercised|KJ Burnt   60|900
2         2        1           Height|Weight|BMI            142|94|35
3         1        2           Minutes Exercised|KJ Burnt   70|1000
4         2        3           Height|Weight|BMI            150|60|20

我想做的是传入一个 TestID 并在标题上看到它的透视

例如。测试 1

MemberID     Minutes Exercised    KJ Burnt
1            60                   900
2            70                   1000

例如。测试 2

MemberID     Height     Weight    BMI
1            142        94        35
3            150        60        20

我查看了所有标准的 Pivoting 示例和动态数据,但它适用于使用相同的字段,我需要一个字段来定义标题,另一个字段来定义结果,不知道如何将它们结合到上面的示例中,任何帮助将不胜感激。

顺便说一句,我不能改变数据的结构。

提前致谢。

【问题讨论】:

  • 是否只有这两个测试,或者在定义列中是否有其他的“列”(|-split)数量可变?
  • 要做的第一件事:殴打您的数据库管理员。如果他问为什么,你会说“第一范式”
  • 嘿肖恩,定义列中的列数可能是可变的,我不知道有多少,这只是一个例子。

标签: sql tsql pivot


【解决方案1】:

我不认为 PIVOT 会起作用;那是为了将列切换到行/反之亦然。在您的示例中,您在单个列中有(可变数量的)列,由竖线字符 (|) 分隔。

所以你需要先做一些准备工作。这项准备工作取决于您有多少不同的测试,以及该单列内可以有多少不同的列。如果只是上面示例中包含 2 或 3 列的两个测试,那么您可以“硬编码”一个或两个将提取数据的查询。如果它比这更多变量,您可能需要一个游标或临时表来完成更复杂的准备工作。

因此,这是一个使用硬编码的 2 个查询集的示例,您必须进行一些小的调整才能使用表数据,而不是变量:

declare @TestID int, @ResultDefinition varchar(100), @MemberID int
set @TestID = 1
set @MemberID = 1

if @TestID = 1 begin

    set @ResultDefinition = '60|900'

    select  @MemberID, 
        LEFT(@ResultDefinition, CHARINDEX('|',  @ResultDefinition)-1) AS [Minutes Exercised],
        SUBSTRING(@ResultDefinition, CHARINDEX('|',  @ResultDefinition)+1, LEN(@ResultDefinition)) AS [KJ Burnt]
    --from  tblResults 
    --where TestID = @TestID

end else begin

    set @ResultDefinition = '142|94|35'

    select  @MemberID, 
        LEFT(@ResultDefinition, CHARINDEX('|',  @ResultDefinition)-1) AS [Height],
        LEFT(SUBSTRING(@ResultDefinition, charindex('|', @ResultDefinition) + 1, LEN(@ResultDefinition) ), charindex('|', SUBSTRING(@ResultDefinition, charindex('|', @ResultDefinition) + 1, LEN(@ResultDefinition) ))-1) as [Weight],
        reverse(left(reverse(@ResultDefinition),  CHARINDEX('|', reverse(@ResultDefinition))-1)) as [BMI]
    --from  tblResults 
    --where TestID = @TestID

end

如果比这更复杂,那么……我其实都不想去那里! :) 这将涉及动态构建表结构,然后用相关数据填充它,然后选择内容。

也许其他人有更优雅的解决方案......

【讨论】:

    【解决方案2】:

    这可以通过PIVOT 来完成,如果您还实现了类似于此的拆分函数:

    拆分函数

    CREATE FUNCTION [dbo].[Split](@String varchar(MAX), @Delimiter char(1))       
    returns @temptable TABLE (items varchar(MAX))       
    as       
    begin      
        declare @idx int       
        declare @slice varchar(8000)       
    
        select @idx = 1       
            if len(@String)<1 or @String is null  return       
    
        while @idx!= 0       
        begin       
            set @idx = charindex(@Delimiter,@String)       
            if @idx!=0       
                set @slice = left(@String,@idx - 1)       
            else       
                set @slice = @String       
    
            if(len(@slice)>0)  
                insert into @temptable(Items) values(@slice)       
    
            set @String = right(@String,len(@String) - @idx)       
            if len(@String) = 0 break       
        end   
    return 
    end;
    

    如果您知道要转换为列的值,则可以使用静态数据透视表对值进行硬编码:

    select *
    from
    (
      select h.memberid,
        h.cols,
        r.results
      from
      (
        select t1.resultid,
          t1.testid,
          t1.memberid, 
          h.items cols,
          row_number() over(partition by memberid order by memberid) rn
        from table1 t1
        cross apply dbo.split(t1.headerdefinition, '|') h
        where t1.testid = 1
      ) h
      left join
      (
        select t1.resultid,
          t1.testid,
          t1.memberid, 
          r.items results,
          row_number() over(partition by memberid order by memberid) rn
        from table1 t1
        cross apply dbo.split(t1.resultdefinition, '|') r
        where t1.testid = 1
      ) r
        on h.memberid = r.memberid
        and h.rn = r.rn
    ) x
    pivot
    (
      max(results)
      for cols in ([Minutes Exercised], [KJ Burnt])
    ) p
    

    SQL Fiddle with Demo

    但我猜你会想要使用动态PIVOT,因为每个条目都有不同数量的列。您的代码将与此类似:

    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX),
        @testid int = 2
    
    select @cols = 
          STUFF((SELECT ',' + QUOTENAME(x.cols) 
                 from
                 (
                   select h.items cols,
                    row_number() over(partition by memberid order by memberid) rn
                   from table1 t1
                   cross apply dbo.split(t1.headerdefinition, '|') h
                   where t1.testid = @testid
                 ) x
                 group by cols, rn
                 order by rn
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    set @query 
      = 'SELECT memberid, ' + @cols + ' from 
         (
            select h.memberid,
              h.cols,
              r.results
            from
            (
              select t1.resultid,
                t1.testid,
                t1.memberid, 
                h.items cols,
                row_number() over(partition by memberid order by memberid) rn
              from table1 t1
              cross apply dbo.split(t1.headerdefinition, ''|'') h
              where t1.testid = '+ cast(@testid as varchar(10)) +'
            ) h
            left join
            (
              select t1.resultid,
                t1.testid,
                t1.memberid, 
                r.items results,
                row_number() over(partition by memberid order by memberid) rn
              from table1 t1
              cross apply dbo.split(t1.resultdefinition, ''|'') r
              where t1.testid = '+ cast(@testid as varchar(10)) +'
            ) r
              on h.memberid = r.memberid
              and h.rn = r.rn
          ) x
          pivot
          (
            max(results)
            for cols in ('+@cols+')
          ) p'
    
    execute(@query)
    

    SQL Fiddle with Demo

    然后根据您传递的@testid,您的结果将是:

    | MEMBERID | MINUTES EXERCISED | KJ BURNT |
    -------------------------------------------
    |        1 |                60 |      900 |
    |        2 |                70 |     1000 |
    

    或者

    | MEMBERID | HEIGHT | WEIGHT | BMI |
    ------------------------------------
    |        1 |    142 |     94 |  35 |
    |        3 |    150 |     60 |  20 |
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-10
      相关资源
      最近更新 更多